@acorex/platform 20.2.4-next.2 → 20.2.4-next.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/common/index.d.ts +30 -4
- package/core/index.d.ts +5 -1
- package/fesm2022/acorex-platform-common.mjs +2 -0
- package/fesm2022/acorex-platform-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-core.mjs +4 -1
- package/fesm2022/acorex-platform-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-builder.mjs +9 -0
- package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-components.mjs +26 -36
- package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity.mjs +770 -243
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-views.mjs +37 -24
- package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
- package/fesm2022/{acorex-platform-themes-default-entity-master-list-view.component-DXGLsVis.mjs → acorex-platform-themes-default-entity-master-list-view.component-D3VUh8K8.mjs} +5 -4
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-D3VUh8K8.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-CVaJzWb2.mjs → acorex-platform-themes-default-entity-master-single-view.component-BMkhNfF4.mjs} +3 -3
- package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-BMkhNfF4.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default.mjs +6 -6
- package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
- package/fesm2022/{acorex-platform-widgets-checkbox-widget-column.component-BNBOATPB.mjs → acorex-platform-widgets-checkbox-widget-column.component-DeKpl0uK.mjs} +1 -2
- package/fesm2022/acorex-platform-widgets-checkbox-widget-column.component-DeKpl0uK.mjs.map +1 -0
- package/fesm2022/{acorex-platform-widgets-file-list-popup.component-B601gPsW.mjs → acorex-platform-widgets-file-list-popup.component-BafU5Lfl.mjs} +4 -2
- package/fesm2022/acorex-platform-widgets-file-list-popup.component-BafU5Lfl.mjs.map +1 -0
- package/fesm2022/acorex-platform-widgets.mjs +231 -71
- package/fesm2022/acorex-platform-widgets.mjs.map +1 -1
- package/layout/builder/index.d.ts +4 -0
- package/layout/components/index.d.ts +1 -5
- package/layout/entity/index.d.ts +4 -5
- package/layout/views/index.d.ts +7 -0
- package/package.json +1 -1
- package/widgets/index.d.ts +25 -2
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-DXGLsVis.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-CVaJzWb2.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-checkbox-widget-column.component-BNBOATPB.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-file-list-popup.component-B601gPsW.mjs.map +0 -1
|
@@ -10,7 +10,7 @@ import { AXPExpressionEvaluatorService, AXPPlatformScope, AXPDistributedEventLis
|
|
|
10
10
|
import * as i2$2 from '@acorex/platform/workflow';
|
|
11
11
|
import { AXPWorkflowService, ofType, createWorkFlowEvent, AXPWorkflowAction, AXPWorkflowModule } from '@acorex/platform/workflow';
|
|
12
12
|
import * as i2 from '@acorex/platform/layout/builder';
|
|
13
|
-
import { AXPPageStatus, AXPWidgetRegistryService, AXPWidgetsCatalog, AXPValueWidgetComponent,
|
|
13
|
+
import { AXPPageStatus, AXPWidgetRegistryService, AXPWidgetsCatalog, AXPValueWidgetComponent, AXPWidgetRendererDirective, AXPLayoutBuilderModule, AXPWidgetGroupEnum, AXPLayoutWidgetComponent, AXPColumnWidgetComponent, AXP_WIDGETS_EDITOR_CATEGORY } from '@acorex/platform/layout/builder';
|
|
14
14
|
import { AXPLayoutThemeService } from '@acorex/platform/themes/shared';
|
|
15
15
|
import { Subject, takeUntil } from 'rxjs';
|
|
16
16
|
import { AXPSessionService, AXPAuthGuard } from '@acorex/platform/auth';
|
|
@@ -265,7 +265,6 @@ class AXPEntityDetailListViewModel {
|
|
|
265
265
|
}, ...(ngDevMode ? [{ debugName: "columns" }] : []));
|
|
266
266
|
this.evaluateExpressions = async (actionData) => {
|
|
267
267
|
const parentData = this.parent.data;
|
|
268
|
-
console.log({ actionData, parentData });
|
|
269
268
|
const scope = {
|
|
270
269
|
context: {
|
|
271
270
|
eval: (path) => {
|
|
@@ -972,6 +971,7 @@ class AXPEntityMasterCreateViewModel {
|
|
|
972
971
|
const createProps = interfaces?.master?.create?.properties?.map(({ name }) => name) ?? [];
|
|
973
972
|
const visibleProperties = properties.filter(({ groupId, schema, name }) => groupId && !schema.hidden && createProps.includes(name));
|
|
974
973
|
const sections = interfaces?.master?.create?.sections?.filter(({ id }) => visibleProperties.some(({ groupId }) => groupId === id)) ?? [];
|
|
974
|
+
console.log({ sections, visibleProperties });
|
|
975
975
|
return sections.map((section) => new AXPEntityCreateViewSectionViewModel(this.entityDef, section));
|
|
976
976
|
}, ...(ngDevMode ? [{ debugName: "sections" }] : []));
|
|
977
977
|
if (!initialData)
|
|
@@ -1157,10 +1157,28 @@ class AXPEntityMasterListViewModel {
|
|
|
1157
1157
|
const visibleProperties = properties.filter(({ schema }) => !schema?.hidden);
|
|
1158
1158
|
const visiblePropNames = new Set(visibleProperties.map(({ name }) => name));
|
|
1159
1159
|
return columns
|
|
1160
|
-
.filter(({ name }) => visiblePropNames.has(name))
|
|
1160
|
+
.filter(({ name, showAs }) => visiblePropNames.has(name) || showAs)
|
|
1161
1161
|
.map((column) => {
|
|
1162
|
-
|
|
1163
|
-
|
|
1162
|
+
if (column.showAs) {
|
|
1163
|
+
const widgetConfig = this.widgetResolver.resolve(column.showAs.type);
|
|
1164
|
+
const property = {
|
|
1165
|
+
...widgetConfig,
|
|
1166
|
+
name: column.name,
|
|
1167
|
+
title: column.title ?? '',
|
|
1168
|
+
schema: {
|
|
1169
|
+
dataType: 'string',
|
|
1170
|
+
interface: {
|
|
1171
|
+
type: column.showAs.type,
|
|
1172
|
+
options: column.showAs.options,
|
|
1173
|
+
},
|
|
1174
|
+
},
|
|
1175
|
+
};
|
|
1176
|
+
return new AXPEntityListViewColumnViewModel(property, column);
|
|
1177
|
+
}
|
|
1178
|
+
else {
|
|
1179
|
+
const property = visibleProperties.find(({ name }) => name === column.name);
|
|
1180
|
+
return new AXPEntityListViewColumnViewModel(property, column);
|
|
1181
|
+
}
|
|
1164
1182
|
});
|
|
1165
1183
|
};
|
|
1166
1184
|
this.visibleColumnCount = () => {
|
|
@@ -1459,8 +1477,7 @@ class AXPEntityMasterListViewModel {
|
|
|
1459
1477
|
const cols = this.view().columns;
|
|
1460
1478
|
const cloned = this.allAvailableColumns().map((c) => {
|
|
1461
1479
|
const column = this.entityDef.columns?.find((cc) => cc.name == c.name);
|
|
1462
|
-
const
|
|
1463
|
-
const col = new AXPEntityListViewColumnViewModel(prop, column);
|
|
1480
|
+
const col = new AXPEntityListViewColumnViewModel(c.property, column);
|
|
1464
1481
|
col.visible = !cols.some((c) => c == col.name) && col.visible != false;
|
|
1465
1482
|
return col;
|
|
1466
1483
|
});
|
|
@@ -1976,7 +1993,6 @@ class AXPEntityMasterSingleViewGroupViewModel {
|
|
|
1976
1993
|
this.group = this.entity.groups?.find((c) => c.id == this.section.id);
|
|
1977
1994
|
this.name = signal(this.group.id, ...(ngDevMode ? [{ debugName: "name" }] : []));
|
|
1978
1995
|
this.isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
1979
|
-
this.type = signal(this.section.type ?? 'section', ...(ngDevMode ? [{ debugName: "type" }] : []));
|
|
1980
1996
|
this.title = computed(() => {
|
|
1981
1997
|
return this.group.title ?? this.group.id;
|
|
1982
1998
|
}, ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
@@ -2556,9 +2572,15 @@ class AXPLayoutAdapterBuilder {
|
|
|
2556
2572
|
this.adapter.pages = [...(this.adapter.pages || []), ...relatedPages];
|
|
2557
2573
|
return this;
|
|
2558
2574
|
}
|
|
2575
|
+
setPages(pages) {
|
|
2576
|
+
this.adapter.pages = pages;
|
|
2577
|
+
return this;
|
|
2578
|
+
}
|
|
2559
2579
|
build() {
|
|
2560
2580
|
return {
|
|
2561
|
-
|
|
2581
|
+
name: `${this.entity?.module}.${this.entity?.name}`,
|
|
2582
|
+
title: `${this.entity?.interfaces?.master?.single?.title}`,
|
|
2583
|
+
label: this.entity?.formats.plural,
|
|
2562
2584
|
actions: [],
|
|
2563
2585
|
breadcrumbs: this.createBreadcrumbs(),
|
|
2564
2586
|
execute: this.createExecuteFunction(),
|
|
@@ -2585,7 +2607,7 @@ class AXPLayoutAdapterBuilder {
|
|
|
2585
2607
|
command: {
|
|
2586
2608
|
name: 'navigate',
|
|
2587
2609
|
options: {
|
|
2588
|
-
path: `/${session.application?.name}/m/${moduleName}/e/${entityName}/${this.rootContext.id}/view`,
|
|
2610
|
+
path: `/${session.application?.name}/m/${moduleName}/e/${entityName}/${this.rootContext.id}/new-view`,
|
|
2589
2611
|
},
|
|
2590
2612
|
},
|
|
2591
2613
|
},
|
|
@@ -2655,40 +2677,29 @@ class AXPBaseRelatedEntityConverter {
|
|
|
2655
2677
|
groups,
|
|
2656
2678
|
};
|
|
2657
2679
|
}
|
|
2658
|
-
createGridLayoutStructure(singleInterface, helpers) {
|
|
2659
|
-
return
|
|
2660
|
-
type: 'grid-layout',
|
|
2661
|
-
options: {
|
|
2662
|
-
grid: {
|
|
2663
|
-
default: {
|
|
2664
|
-
gridTemplateColumns: 'repeat(12, 1fr)',
|
|
2665
|
-
gridTemplateRows: 'repeat(1, 1fr)',
|
|
2666
|
-
gap: '20px',
|
|
2667
|
-
},
|
|
2668
|
-
},
|
|
2669
|
-
},
|
|
2670
|
-
children: singleInterface?.sections.map((s) => ({
|
|
2671
|
-
type: 'grid-item-layout',
|
|
2672
|
-
name: s.id,
|
|
2673
|
-
options: {
|
|
2674
|
-
colSpan: s.layout?.positions?.lg?.colSpan ?? 12,
|
|
2675
|
-
colStart: s.layout?.positions?.lg?.colStart,
|
|
2676
|
-
colEnd: s.layout?.positions?.lg?.colEnd,
|
|
2677
|
-
},
|
|
2678
|
-
children: [
|
|
2679
|
-
{
|
|
2680
|
-
type: 'fieldset-layout',
|
|
2681
|
-
options: {
|
|
2682
|
-
title: helpers.getGroupById(s.id)?.title ?? '',
|
|
2683
|
-
collapsible: true,
|
|
2684
|
-
},
|
|
2685
|
-
children: [this.createPropertyGrid(s.id, helpers)],
|
|
2686
|
-
},
|
|
2687
|
-
],
|
|
2688
|
-
})),
|
|
2689
|
-
};
|
|
2680
|
+
async createGridLayoutStructure(singleInterface, helpers, evaluateExpressions) {
|
|
2681
|
+
return await this.createGridLayoutStructureInternal(singleInterface, helpers, evaluateExpressions);
|
|
2690
2682
|
}
|
|
2691
|
-
|
|
2683
|
+
//#region ---- Hidden Evaluation Helpers ----
|
|
2684
|
+
async getVisiblePropertiesByGroupId(sectionId, helpers, evaluateExpressions) {
|
|
2685
|
+
const properties = helpers.getPropertyByGroupId(sectionId) ?? [];
|
|
2686
|
+
const evaluated = await Promise.all(properties.map(async (property) => {
|
|
2687
|
+
let hidden = property?.schema?.hidden;
|
|
2688
|
+
if (typeof hidden === 'string' && evaluateExpressions) {
|
|
2689
|
+
try {
|
|
2690
|
+
const result = await evaluateExpressions({ hidden });
|
|
2691
|
+
hidden = result.hidden;
|
|
2692
|
+
}
|
|
2693
|
+
catch {
|
|
2694
|
+
hidden = false;
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
return { property, hidden: !!hidden };
|
|
2698
|
+
}));
|
|
2699
|
+
return evaluated.filter((x) => !x.hidden).map((x) => x.property);
|
|
2700
|
+
}
|
|
2701
|
+
async createPropertyGrid(sectionId, helpers, evaluateExpressions) {
|
|
2702
|
+
const visibleProperties = await this.getVisiblePropertiesByGroupId(sectionId, helpers, evaluateExpressions);
|
|
2692
2703
|
return {
|
|
2693
2704
|
type: 'grid-layout',
|
|
2694
2705
|
mode: 'edit',
|
|
@@ -2701,10 +2712,7 @@ class AXPBaseRelatedEntityConverter {
|
|
|
2701
2712
|
},
|
|
2702
2713
|
},
|
|
2703
2714
|
},
|
|
2704
|
-
children:
|
|
2705
|
-
.getPropertyByGroupId(sectionId)
|
|
2706
|
-
.filter((property) => !property.schema.hidden)
|
|
2707
|
-
.map((p) => {
|
|
2715
|
+
children: visibleProperties.map((p) => {
|
|
2708
2716
|
const layout = helpers.getPropertyLayout(p.name);
|
|
2709
2717
|
return {
|
|
2710
2718
|
type: 'grid-item-layout',
|
|
@@ -2719,6 +2727,7 @@ class AXPBaseRelatedEntityConverter {
|
|
|
2719
2727
|
type: 'form-field',
|
|
2720
2728
|
options: {
|
|
2721
2729
|
label: p.title,
|
|
2730
|
+
showLabel: layout?.label?.visible ?? true,
|
|
2722
2731
|
},
|
|
2723
2732
|
children: [
|
|
2724
2733
|
{
|
|
@@ -2738,12 +2747,58 @@ class AXPBaseRelatedEntityConverter {
|
|
|
2738
2747
|
}),
|
|
2739
2748
|
};
|
|
2740
2749
|
}
|
|
2750
|
+
async createGridLayoutStructureInternal(singleInterface, helpers, evaluateExpressions) {
|
|
2751
|
+
return {
|
|
2752
|
+
type: 'grid-layout',
|
|
2753
|
+
options: {
|
|
2754
|
+
grid: {
|
|
2755
|
+
default: {
|
|
2756
|
+
gridTemplateColumns: 'repeat(12, 1fr)',
|
|
2757
|
+
gridTemplateRows: 'repeat(1, 1fr)',
|
|
2758
|
+
gap: '20px',
|
|
2759
|
+
},
|
|
2760
|
+
},
|
|
2761
|
+
},
|
|
2762
|
+
children: await Promise.all(singleInterface?.sections.map(async (s) => ({
|
|
2763
|
+
type: 'grid-item-layout',
|
|
2764
|
+
name: s.id,
|
|
2765
|
+
options: {
|
|
2766
|
+
colSpan: s.layout?.positions?.lg?.colSpan ?? 12,
|
|
2767
|
+
colStart: s.layout?.positions?.lg?.colStart,
|
|
2768
|
+
colEnd: s.layout?.positions?.lg?.colEnd,
|
|
2769
|
+
},
|
|
2770
|
+
children: [
|
|
2771
|
+
{
|
|
2772
|
+
type: 'fieldset-layout',
|
|
2773
|
+
options: {
|
|
2774
|
+
title: helpers.getGroupById(s.id)?.title ?? '',
|
|
2775
|
+
collapsible: true,
|
|
2776
|
+
},
|
|
2777
|
+
children: [await this.createPropertyGrid(s.id, helpers, evaluateExpressions)],
|
|
2778
|
+
},
|
|
2779
|
+
],
|
|
2780
|
+
}))),
|
|
2781
|
+
};
|
|
2782
|
+
}
|
|
2741
2783
|
}
|
|
2742
2784
|
|
|
2743
2785
|
class AXPPageDetailsConverter extends AXPBaseRelatedEntityConverter {
|
|
2744
2786
|
async convert(relatedEntity, context) {
|
|
2745
2787
|
const { entityDef } = await this.getEntityDefinition(relatedEntity, context.entityResolver);
|
|
2746
|
-
const
|
|
2788
|
+
const baseHelpers = this.createEntityHelpers(entityDef);
|
|
2789
|
+
// If referenced strategy with mapping and detail type, exclude mapped properties from UI
|
|
2790
|
+
const p = relatedEntity?.persistence || {};
|
|
2791
|
+
const strategy = p.strategy || 'embedded';
|
|
2792
|
+
const mappedTargets = Object.keys(p.map || {}).map((k) => (k || '').split('.')[0]);
|
|
2793
|
+
const helpers = {
|
|
2794
|
+
...baseHelpers,
|
|
2795
|
+
getPropertyByGroupId: (groupId) => {
|
|
2796
|
+
const props = baseHelpers.getPropertyByGroupId(groupId) ?? [];
|
|
2797
|
+
if (strategy !== 'referenced' || mappedTargets.length === 0)
|
|
2798
|
+
return props;
|
|
2799
|
+
return props.filter((prop) => !mappedTargets.includes(prop?.name));
|
|
2800
|
+
},
|
|
2801
|
+
};
|
|
2747
2802
|
const evaluateExpressions = async (actionData) => {
|
|
2748
2803
|
const scope = {
|
|
2749
2804
|
context: {
|
|
@@ -2754,17 +2809,25 @@ class AXPPageDetailsConverter extends AXPBaseRelatedEntityConverter {
|
|
|
2754
2809
|
};
|
|
2755
2810
|
return await context.expressionEvaluator.evaluate(actionData, scope);
|
|
2756
2811
|
};
|
|
2812
|
+
// Build related tabs for the related entity page (mirrors main-entity tab building)
|
|
2813
|
+
const tabDetailEntities = entityDef?.relatedEntities?.filter((re) => !re.hidden && re.layout?.type === 'tab-detail');
|
|
2814
|
+
const tabListEntities = entityDef?.relatedEntities?.filter((re) => !re.hidden && (!re.layout?.type || re.layout?.type === 'tab-list'));
|
|
2815
|
+
const factory = new AXPRelatedEntityConverterFactory();
|
|
2816
|
+
const tabDetailTabs = await this.buildTabDetails(factory, tabDetailEntities ?? [], context);
|
|
2817
|
+
const tabListTabs = await this.buildTabLists(factory, tabListEntities ?? [], context);
|
|
2757
2818
|
return {
|
|
2758
2819
|
id: entityDef?.name ?? '',
|
|
2759
|
-
title:
|
|
2820
|
+
title: `${context.rootTitle}`,
|
|
2760
2821
|
label: relatedEntity.title ?? entityDef?.formats.displayName ?? '',
|
|
2761
2822
|
icon: relatedEntity.icon || entityDef.icon,
|
|
2762
2823
|
settings: this.createPageSettings(),
|
|
2763
2824
|
load: this.createLoadFunction(entityDef, relatedEntity, evaluateExpressions),
|
|
2764
2825
|
execute: this.createExecuteFunction(entityDef),
|
|
2765
|
-
|
|
2826
|
+
// tabs: [...tabDetailTabs, ...tabListTabs],
|
|
2827
|
+
content: [await this.createGridLayoutStructure(helpers.singleInterface, helpers, evaluateExpressions)],
|
|
2766
2828
|
};
|
|
2767
2829
|
}
|
|
2830
|
+
//#region ---- Utility Methods ----
|
|
2768
2831
|
createPageSettings() {
|
|
2769
2832
|
return {
|
|
2770
2833
|
commands: {
|
|
@@ -2787,7 +2850,7 @@ class AXPPageDetailsConverter extends AXPBaseRelatedEntityConverter {
|
|
|
2787
2850
|
return async (context) => {
|
|
2788
2851
|
const fn = entityDef?.queries.byKey?.execute;
|
|
2789
2852
|
const conditionValues = relatedEntity.conditions?.map((c) => c.value) ?? [];
|
|
2790
|
-
const evaluatedConditionValues = await
|
|
2853
|
+
const evaluatedConditionValues = await Promise.all(conditionValues.map((c) => evaluateExpressions(c)));
|
|
2791
2854
|
const id = evaluatedConditionValues[0];
|
|
2792
2855
|
const result = await fn(id);
|
|
2793
2856
|
return { success: true, result };
|
|
@@ -2808,6 +2871,42 @@ class AXPPageDetailsConverter extends AXPBaseRelatedEntityConverter {
|
|
|
2808
2871
|
}
|
|
2809
2872
|
};
|
|
2810
2873
|
}
|
|
2874
|
+
//#endregion
|
|
2875
|
+
//#region ---- Tab Builders ----
|
|
2876
|
+
/**
|
|
2877
|
+
* Builds tab-detail items for a related entity page using the shared converters.
|
|
2878
|
+
*/
|
|
2879
|
+
async buildTabDetails(factory, tabDetailEntities, ctx) {
|
|
2880
|
+
if (!ctx?.entityResolver || !tabDetailEntities?.length) {
|
|
2881
|
+
return [];
|
|
2882
|
+
}
|
|
2883
|
+
const tabs = [];
|
|
2884
|
+
for (const re of tabDetailEntities) {
|
|
2885
|
+
const converter = factory.createTabDetailsConverter();
|
|
2886
|
+
tabs.push(await converter.convert(re, {
|
|
2887
|
+
entityResolver: ctx.entityResolver,
|
|
2888
|
+
}));
|
|
2889
|
+
}
|
|
2890
|
+
return tabs;
|
|
2891
|
+
}
|
|
2892
|
+
/**
|
|
2893
|
+
* Builds tab-list items for a related entity page using the shared converters with context-aware filters/actions.
|
|
2894
|
+
*/
|
|
2895
|
+
async buildTabLists(factory, tabListEntities, ctx) {
|
|
2896
|
+
if (!ctx?.entityResolver || !tabListEntities?.length) {
|
|
2897
|
+
return [];
|
|
2898
|
+
}
|
|
2899
|
+
const tabs = [];
|
|
2900
|
+
for (const re of tabListEntities) {
|
|
2901
|
+
const converter = factory.createTabListConverter();
|
|
2902
|
+
tabs.push(await converter.convert(re, {
|
|
2903
|
+
entityResolver: ctx.entityResolver,
|
|
2904
|
+
expressionEvaluator: ctx.expressionEvaluator,
|
|
2905
|
+
context: ctx.context,
|
|
2906
|
+
}));
|
|
2907
|
+
}
|
|
2908
|
+
return tabs;
|
|
2909
|
+
}
|
|
2811
2910
|
}
|
|
2812
2911
|
|
|
2813
2912
|
class AXPPageListConverter extends AXPBaseRelatedEntityConverter {
|
|
@@ -2823,6 +2922,7 @@ class AXPPageListConverter extends AXPBaseRelatedEntityConverter {
|
|
|
2823
2922
|
};
|
|
2824
2923
|
return await context.expressionEvaluator.evaluate(actionData, scope);
|
|
2825
2924
|
};
|
|
2925
|
+
const evaluatedActions = await evaluateExpressions(relatedEntity?.actions);
|
|
2826
2926
|
const filters = relatedEntity.conditions?.map(async (c) => {
|
|
2827
2927
|
const value = await evaluateExpressions(c.value);
|
|
2828
2928
|
return {
|
|
@@ -2837,17 +2937,13 @@ class AXPPageListConverter extends AXPBaseRelatedEntityConverter {
|
|
|
2837
2937
|
title: `${context.rootTitle}`,
|
|
2838
2938
|
label: relatedEntity.title,
|
|
2839
2939
|
icon: relatedEntity.icon || entityDef.icon,
|
|
2840
|
-
actions: this.mergeActions(entityDef,
|
|
2940
|
+
actions: this.mergeActions(entityDef, evaluatedActions)
|
|
2841
2941
|
?.filter((a) => a.priority === 'primary')
|
|
2842
2942
|
?.map((a) => {
|
|
2843
2943
|
return {
|
|
2844
2944
|
...a,
|
|
2845
2945
|
zone: 'header',
|
|
2846
|
-
|
|
2847
|
-
// a.scope === AXPEntityCommandScope.Selected
|
|
2848
|
-
// ? "{{widget.find('table').outputs().selectedItem().length > 0}}"
|
|
2849
|
-
// : true,
|
|
2850
|
-
visible: '{{context.eval("table")}}',
|
|
2946
|
+
visible: !a.hidden,
|
|
2851
2947
|
priority: 'primary',
|
|
2852
2948
|
name: a.name,
|
|
2853
2949
|
title: a.title,
|
|
@@ -2865,7 +2961,7 @@ class AXPPageListConverter extends AXPBaseRelatedEntityConverter {
|
|
|
2865
2961
|
execute: async (command, executeContext) => {
|
|
2866
2962
|
try {
|
|
2867
2963
|
const commandName = command.name.split('&')[0];
|
|
2868
|
-
const mergedActions = this.mergeActions(entityDef,
|
|
2964
|
+
const mergedActions = this.mergeActions(entityDef, evaluatedActions);
|
|
2869
2965
|
const action = mergedActions.find((a) => {
|
|
2870
2966
|
return a.name === commandName || a.name.split('&')[0] === commandName;
|
|
2871
2967
|
});
|
|
@@ -2880,10 +2976,6 @@ class AXPPageListConverter extends AXPBaseRelatedEntityConverter {
|
|
|
2880
2976
|
},
|
|
2881
2977
|
};
|
|
2882
2978
|
}
|
|
2883
|
-
let evaluatedOptions = command.options;
|
|
2884
|
-
if (action.options) {
|
|
2885
|
-
evaluatedOptions = await evaluateExpressions(action.options);
|
|
2886
|
-
}
|
|
2887
2979
|
await context.workflowService.execute(commandName, {
|
|
2888
2980
|
entity: getEntityInfo(entityDef).source,
|
|
2889
2981
|
entityInfo: {
|
|
@@ -2895,8 +2987,8 @@ class AXPPageListConverter extends AXPBaseRelatedEntityConverter {
|
|
|
2895
2987
|
},
|
|
2896
2988
|
data: action.scope == AXPEntityCommandScope.Selected
|
|
2897
2989
|
? executeContext
|
|
2898
|
-
:
|
|
2899
|
-
options:
|
|
2990
|
+
: action.options?.['process']?.data || null,
|
|
2991
|
+
options: action.options,
|
|
2900
2992
|
metadata: action.metadata,
|
|
2901
2993
|
});
|
|
2902
2994
|
return { success: true };
|
|
@@ -2925,14 +3017,15 @@ class AXPPageListConverter extends AXPBaseRelatedEntityConverter {
|
|
|
2925
3017
|
options: {
|
|
2926
3018
|
entity: relatedEntity.entity,
|
|
2927
3019
|
showEntityActions: false,
|
|
3020
|
+
actions: evaluatedActions,
|
|
2928
3021
|
},
|
|
2929
3022
|
},
|
|
2930
3023
|
],
|
|
2931
3024
|
};
|
|
2932
3025
|
}
|
|
2933
|
-
mergeActions(entityDef,
|
|
3026
|
+
mergeActions(entityDef, relatedEntityActions) {
|
|
2934
3027
|
const originalList = entityDef?.interfaces?.master?.list?.actions ?? [];
|
|
2935
|
-
const relatedEntityActionList =
|
|
3028
|
+
const relatedEntityActionList = relatedEntityActions ?? [];
|
|
2936
3029
|
// Create a map to track which actions from relatedEntityActionList have been used
|
|
2937
3030
|
const usedOverrideActions = new Set();
|
|
2938
3031
|
// Start with original actions, applying overrides where they exist
|
|
@@ -2964,12 +3057,25 @@ class AXPPageListConverter extends AXPBaseRelatedEntityConverter {
|
|
|
2964
3057
|
class AXPTabDetailsConverter extends AXPBaseRelatedEntityConverter {
|
|
2965
3058
|
async convert(relatedEntity, context) {
|
|
2966
3059
|
const { entityDef } = await this.getEntityDefinition(relatedEntity, context.entityResolver);
|
|
2967
|
-
const
|
|
3060
|
+
const baseHelpers = this.createEntityHelpers(entityDef);
|
|
3061
|
+
// If referenced strategy with mapping and detail type, exclude mapped properties from UI
|
|
3062
|
+
const p = relatedEntity?.persistence || {};
|
|
3063
|
+
const strategy = p.strategy || 'embedded';
|
|
3064
|
+
const mappedTargets = Object.keys(p.map || {}).map((k) => (k || '').split('.')[0]);
|
|
3065
|
+
const helpers = {
|
|
3066
|
+
...baseHelpers,
|
|
3067
|
+
getPropertyByGroupId: (groupId) => {
|
|
3068
|
+
const props = baseHelpers.getPropertyByGroupId(groupId) ?? [];
|
|
3069
|
+
if (strategy !== 'referenced' || mappedTargets.length === 0)
|
|
3070
|
+
return props;
|
|
3071
|
+
return props.filter((prop) => !mappedTargets.includes(prop?.name));
|
|
3072
|
+
},
|
|
3073
|
+
};
|
|
2968
3074
|
return {
|
|
2969
3075
|
id: entityDef?.name ?? '',
|
|
2970
3076
|
title: relatedEntity.title ?? entityDef?.title ?? '',
|
|
2971
3077
|
icon: relatedEntity.icon || entityDef.icon,
|
|
2972
|
-
content: [this.createGridLayoutStructure(helpers.singleInterface, helpers)],
|
|
3078
|
+
content: [await this.createGridLayoutStructure(helpers.singleInterface, helpers)],
|
|
2973
3079
|
};
|
|
2974
3080
|
}
|
|
2975
3081
|
}
|
|
@@ -3066,41 +3172,270 @@ class AXPMainEntityContentBuilder {
|
|
|
3066
3172
|
this.workflowService = inject(AXPWorkflowService);
|
|
3067
3173
|
this.commandService = inject(AXPCommandService);
|
|
3068
3174
|
}
|
|
3069
|
-
async build(entity, rootContext, dependencies) {
|
|
3175
|
+
async build(entity, rootContext, dependencies, rootTitle) {
|
|
3070
3176
|
const groups = entity?.groups ?? [];
|
|
3071
3177
|
const singleInterface = entity?.interfaces?.master?.single;
|
|
3178
|
+
// Accumulate groups from main and merge-detail related entities for validation/title lookup
|
|
3179
|
+
const allGroups = [...groups];
|
|
3180
|
+
// Prepare merge-details structures (sections and properties per section)
|
|
3181
|
+
const mergeDetailEntities = entity?.relatedEntities?.filter((re) => !re.hidden && re.layout?.type === 'merge-detail');
|
|
3182
|
+
const mainPropsByGroup = (entity?.properties ?? []).reduce((acc, p) => {
|
|
3183
|
+
if (!acc[p.groupId])
|
|
3184
|
+
acc[p.groupId] = [];
|
|
3185
|
+
acc[p.groupId].push(p);
|
|
3186
|
+
return acc;
|
|
3187
|
+
}, {});
|
|
3188
|
+
// Will hold merged properties per group (section)
|
|
3189
|
+
const mergedPropsByGroup = { ...mainPropsByGroup };
|
|
3190
|
+
// Collect sections from main interface (validated later) and related ones for ordering
|
|
3191
|
+
const mainSections = singleInterface?.sections ?? [];
|
|
3192
|
+
// Track extra sections with entity-level ordering context
|
|
3193
|
+
const beforeExtraSections = [];
|
|
3194
|
+
const middleExtraSections = [];
|
|
3195
|
+
const afterExtraSections = [];
|
|
3196
|
+
const beforeExtraIds = new Set();
|
|
3197
|
+
const middleExtraIds = new Set();
|
|
3198
|
+
const afterExtraIds = new Set();
|
|
3199
|
+
// Effective order overrides for main sections when merged with BEFORE related entities
|
|
3200
|
+
let beforeOverrideSectionOrders = {};
|
|
3201
|
+
// Collect tab-list tabs from merge-detail related entities to merge with main tabs
|
|
3202
|
+
const nestedTabListTabs = [];
|
|
3203
|
+
// Helper to append related-only sections into before/after buckets
|
|
3204
|
+
const queueRelatedSection = (section, position, entityOrder) => {
|
|
3205
|
+
if (!section)
|
|
3206
|
+
return;
|
|
3207
|
+
if (position === 'before') {
|
|
3208
|
+
if (!beforeExtraIds.has(section.id)) {
|
|
3209
|
+
beforeExtraIds.add(section.id);
|
|
3210
|
+
beforeExtraSections.push({ section, entityOrder });
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
else if (position === 'middle') {
|
|
3214
|
+
if (!middleExtraIds.has(section.id)) {
|
|
3215
|
+
middleExtraIds.add(section.id);
|
|
3216
|
+
middleExtraSections.push({ section });
|
|
3217
|
+
}
|
|
3218
|
+
}
|
|
3219
|
+
else {
|
|
3220
|
+
if (!afterExtraIds.has(section.id)) {
|
|
3221
|
+
afterExtraIds.add(section.id);
|
|
3222
|
+
afterExtraSections.push({ section, entityOrder });
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
};
|
|
3226
|
+
// Resolve and merge merge-detail entities
|
|
3227
|
+
// Sort related entities by position group then by layout.order
|
|
3228
|
+
if (mergeDetailEntities?.length && dependencies?.entityResolver) {
|
|
3229
|
+
const beforeEntities = (mergeDetailEntities || [])
|
|
3230
|
+
.filter((re) => (re.layout?.position ?? 'middle') === 'before')
|
|
3231
|
+
.sort((a, b) => (a.layout?.order ?? Number.MAX_SAFE_INTEGER) - (b.layout?.order ?? Number.MAX_SAFE_INTEGER));
|
|
3232
|
+
const middleEntities = (mergeDetailEntities || []).filter((re) => (re.layout?.position ?? 'middle') === 'middle');
|
|
3233
|
+
const afterEntities = (mergeDetailEntities || [])
|
|
3234
|
+
.filter((re) => (re.layout?.position ?? 'middle') === 'after')
|
|
3235
|
+
.sort((a, b) => (a.layout?.order ?? Number.MAX_SAFE_INTEGER) - (b.layout?.order ?? Number.MAX_SAFE_INTEGER));
|
|
3236
|
+
const processRelated = async (relatedEntity) => {
|
|
3237
|
+
try {
|
|
3238
|
+
const [moduleName, entityName] = relatedEntity.entity.split('.');
|
|
3239
|
+
const entityDef = await dependencies.entityResolver.resolve(moduleName, entityName);
|
|
3240
|
+
if (!entityDef) {
|
|
3241
|
+
return;
|
|
3242
|
+
}
|
|
3243
|
+
const relSingle = entityDef?.interfaces?.master?.single;
|
|
3244
|
+
const relGroups = entityDef?.groups ?? [];
|
|
3245
|
+
// Merge related groups into allGroups (avoid duplicates by id)
|
|
3246
|
+
for (const rg of relGroups) {
|
|
3247
|
+
if (!allGroups.some((g) => g.id === rg.id)) {
|
|
3248
|
+
allGroups.push(rg);
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
// Collect nested tab-list related entities from this merge-detail entity and convert to tabs
|
|
3252
|
+
try {
|
|
3253
|
+
const nestedTabListEntities = entityDef?.relatedEntities?.filter((re) => !re.hidden && (!re.layout?.type || re.layout?.type === 'tab-list'));
|
|
3254
|
+
if (nestedTabListEntities?.length) {
|
|
3255
|
+
const converter = this.relatedEntityConverterFactory.createTabListConverter();
|
|
3256
|
+
const nestedDataPath = relatedEntity?.persistence?.dataPath || entityName;
|
|
3257
|
+
const nestedContext = get(rootContext, nestedDataPath) ?? rootContext;
|
|
3258
|
+
for (const nre of nestedTabListEntities) {
|
|
3259
|
+
try {
|
|
3260
|
+
const tab = await converter.convert(nre, {
|
|
3261
|
+
entityResolver: dependencies.entityResolver,
|
|
3262
|
+
expressionEvaluator: dependencies.expressionEvaluator,
|
|
3263
|
+
// Evaluate conditions relative to the merge-detail entity's dataPath
|
|
3264
|
+
context: nestedContext,
|
|
3265
|
+
});
|
|
3266
|
+
nestedTabListTabs.push(tab);
|
|
3267
|
+
}
|
|
3268
|
+
catch { }
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
catch { }
|
|
3273
|
+
// Build related props map per group with metadata: __dataPath and __layout
|
|
3274
|
+
// If referenced strategy with mapping, exclude mapped target property names from UI props
|
|
3275
|
+
const p = relatedEntity?.persistence || {};
|
|
3276
|
+
const strategy = p.strategy || 'embedded';
|
|
3277
|
+
const mappedTargets = Object.keys(p.map || {}).map((k) => (k || '').split('.')[0]);
|
|
3278
|
+
const relPropsByGroup = (entityDef?.properties ?? []).reduce((acc, p) => {
|
|
3279
|
+
if (strategy === 'referenced' &&
|
|
3280
|
+
(relatedEntity?.layout?.type === 'merge-detail' ||
|
|
3281
|
+
relatedEntity?.layout?.type === 'page-detail' ||
|
|
3282
|
+
relatedEntity?.layout?.type === 'tab-detail') &&
|
|
3283
|
+
mappedTargets.includes(p.name)) {
|
|
3284
|
+
return acc; // skip mapped properties
|
|
3285
|
+
}
|
|
3286
|
+
if (!acc[p.groupId])
|
|
3287
|
+
acc[p.groupId] = [];
|
|
3288
|
+
// Clone shallow to not mutate original definitions
|
|
3289
|
+
const propClone = { ...p };
|
|
3290
|
+
propClone.__dataPath = relatedEntity?.persistence?.dataPath || entityName;
|
|
3291
|
+
propClone.__layout = relSingle?.properties?.find((x) => x.name === p.name)?.layout;
|
|
3292
|
+
acc[p.groupId].push(propClone);
|
|
3293
|
+
return acc;
|
|
3294
|
+
}, {});
|
|
3295
|
+
// Merge properties by section id
|
|
3296
|
+
const position = relatedEntity.layout?.position ?? 'middle';
|
|
3297
|
+
const entityOrder = relatedEntity.layout?.order ?? Number.MAX_SAFE_INTEGER;
|
|
3298
|
+
const relSections = (relSingle?.sections ?? []).filter((s) => relGroups.some((g) => g.id === s.id));
|
|
3299
|
+
for (const rs of relSections) {
|
|
3300
|
+
const sectionId = rs.id;
|
|
3301
|
+
const mainHasSection = !!mainSections?.some((s) => s.id === sectionId);
|
|
3302
|
+
const relProps = relPropsByGroup[sectionId] ?? [];
|
|
3303
|
+
if (mainHasSection) {
|
|
3304
|
+
const current = mergedPropsByGroup[sectionId] ?? [];
|
|
3305
|
+
mergedPropsByGroup[sectionId] =
|
|
3306
|
+
position === 'before' ? [...relProps, ...current] : [...current, ...relProps];
|
|
3307
|
+
// Capture override order for main section when position is 'before'
|
|
3308
|
+
if (position === 'before') {
|
|
3309
|
+
const order = rs?.order ?? 0;
|
|
3310
|
+
const prev = beforeOverrideSectionOrders[sectionId] ?? Number.MAX_SAFE_INTEGER;
|
|
3311
|
+
beforeOverrideSectionOrders[sectionId] = Math.min(prev, order);
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
else {
|
|
3315
|
+
// Section isn't in main; queue section for rendering and set its props
|
|
3316
|
+
if (!mergedPropsByGroup[sectionId]) {
|
|
3317
|
+
mergedPropsByGroup[sectionId] = [];
|
|
3318
|
+
}
|
|
3319
|
+
mergedPropsByGroup[sectionId] =
|
|
3320
|
+
position === 'before'
|
|
3321
|
+
? [...(relProps ?? []), ...(mergedPropsByGroup[sectionId] ?? [])]
|
|
3322
|
+
: [...(mergedPropsByGroup[sectionId] ?? []), ...(relProps ?? [])];
|
|
3323
|
+
queueRelatedSection(rs, position, entityOrder);
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
catch {
|
|
3328
|
+
// Silently ignore failures to resolve or merge
|
|
3329
|
+
}
|
|
3330
|
+
};
|
|
3331
|
+
// Process BEFORE entities first (sorted), then AFTER entities
|
|
3332
|
+
for (const re of beforeEntities) {
|
|
3333
|
+
await processRelated(re);
|
|
3334
|
+
}
|
|
3335
|
+
for (const re of middleEntities) {
|
|
3336
|
+
await processRelated(re);
|
|
3337
|
+
}
|
|
3338
|
+
for (const re of afterEntities) {
|
|
3339
|
+
await processRelated(re);
|
|
3340
|
+
}
|
|
3341
|
+
}
|
|
3072
3342
|
const getGroupById = (id) => {
|
|
3073
3343
|
return groups.find((s) => s.id === id);
|
|
3074
3344
|
};
|
|
3075
3345
|
const filterValidSections = (sections) => {
|
|
3076
3346
|
return (sections?.filter((section) => {
|
|
3077
|
-
return
|
|
3347
|
+
return allGroups.some((group) => group.id === section.id);
|
|
3078
3348
|
}) ?? []);
|
|
3079
3349
|
};
|
|
3080
|
-
|
|
3081
|
-
|
|
3350
|
+
// Utility: sort related-only sections by entity order then section order
|
|
3351
|
+
const sortRelatedSections = (sections) => [...(sections ?? [])]
|
|
3352
|
+
.filter((e) => !!e?.section)
|
|
3353
|
+
.sort((a, b) => {
|
|
3354
|
+
const ae = a.entityOrder ?? Number.MAX_SAFE_INTEGER;
|
|
3355
|
+
const be = b.entityOrder ?? Number.MAX_SAFE_INTEGER;
|
|
3356
|
+
if (ae !== be)
|
|
3357
|
+
return ae - be;
|
|
3358
|
+
const ao = a.section?.order ?? 0;
|
|
3359
|
+
const bo = b.section?.order ?? 0;
|
|
3360
|
+
return ao - bo;
|
|
3361
|
+
})
|
|
3362
|
+
.map((e) => e.section);
|
|
3363
|
+
// Determine effective order for main sections: if any BEFORE related merged that section, use its order; otherwise keep main order
|
|
3364
|
+
const computeEffectiveMainOrder = (s) => {
|
|
3365
|
+
const defaultOrder = s?.order ?? 0;
|
|
3366
|
+
const beforeOverride = beforeOverrideSectionOrders?.[s?.id ?? ''];
|
|
3367
|
+
return beforeOverride != null ? beforeOverride : defaultOrder;
|
|
3082
3368
|
};
|
|
3083
|
-
|
|
3084
|
-
|
|
3369
|
+
// Build combined section list:
|
|
3370
|
+
// 1) related-before (entity-sorted)
|
|
3371
|
+
// 2) middle block: main sections + related-middle-only sections sorted by their section.order (with effective order for main)
|
|
3372
|
+
// 3) related-after (entity-sorted)
|
|
3373
|
+
const beforeSections = sortRelatedSections(beforeExtraSections);
|
|
3374
|
+
const mainSectionsList = [...filterValidSections(mainSections)];
|
|
3375
|
+
const middleOnlySections = middleExtraSections.map((e) => e.section);
|
|
3376
|
+
const middleBlock = [...mainSectionsList, ...middleOnlySections].sort((a, b) => {
|
|
3377
|
+
const ao = computeEffectiveMainOrder(a);
|
|
3378
|
+
const bo = computeEffectiveMainOrder(b);
|
|
3379
|
+
return ao - bo;
|
|
3380
|
+
});
|
|
3381
|
+
const afterSections = sortRelatedSections(afterExtraSections);
|
|
3382
|
+
const combinedSections = [...beforeSections, ...middleBlock, ...afterSections];
|
|
3383
|
+
// Debug: Log final sorted sections for verification
|
|
3384
|
+
try {
|
|
3385
|
+
const beforeEntityOrderMap = new Map(beforeExtraSections.map((e) => [e.section?.id, e.entityOrder ?? Number.MAX_SAFE_INTEGER]));
|
|
3386
|
+
const afterEntityOrderMap = new Map(afterExtraSections.map((e) => [e.section?.id, e.entityOrder ?? Number.MAX_SAFE_INTEGER]));
|
|
3387
|
+
const debugSections = [
|
|
3388
|
+
...beforeSections.map((s) => ({
|
|
3389
|
+
bucket: 'before',
|
|
3390
|
+
id: s?.id,
|
|
3391
|
+
sectionOrder: s?.order ?? 0,
|
|
3392
|
+
entityOrder: beforeEntityOrderMap.get(s?.id) ?? Number.MAX_SAFE_INTEGER,
|
|
3393
|
+
})),
|
|
3394
|
+
...middleBlock.map((s) => ({
|
|
3395
|
+
bucket: 'middle',
|
|
3396
|
+
id: s?.id,
|
|
3397
|
+
sectionOrder: s?.order ?? 0,
|
|
3398
|
+
effectiveOrder: computeEffectiveMainOrder(s),
|
|
3399
|
+
})),
|
|
3400
|
+
...afterSections.map((s) => ({
|
|
3401
|
+
bucket: 'after',
|
|
3402
|
+
id: s?.id,
|
|
3403
|
+
sectionOrder: s?.order ?? 0,
|
|
3404
|
+
entityOrder: afterEntityOrderMap.get(s?.id) ?? Number.MAX_SAFE_INTEGER,
|
|
3405
|
+
})),
|
|
3406
|
+
];
|
|
3407
|
+
// eslint-disable-next-line no-console
|
|
3408
|
+
console.debug('[AXP] merge-details sorted sections', debugSections);
|
|
3409
|
+
}
|
|
3410
|
+
catch { }
|
|
3411
|
+
// Create expression evaluator for actions
|
|
3412
|
+
const evaluateExpressions = dependencies?.expressionEvaluator
|
|
3413
|
+
? this.createExpressionEvaluator(rootContext, dependencies.expressionEvaluator)
|
|
3414
|
+
: null;
|
|
3415
|
+
const getVisiblePropertyByGroupId = async (groupId) => {
|
|
3416
|
+
return (mergedPropsByGroup[groupId] ?? []);
|
|
3417
|
+
};
|
|
3418
|
+
const getPropertyLayout = (name, prop) => {
|
|
3419
|
+
// Prefer per-property layout (for merged related props), fallback to main entity layout
|
|
3420
|
+
return prop?.__layout ?? singleInterface?.properties?.find((p) => p.name === name)?.layout;
|
|
3085
3421
|
};
|
|
3086
3422
|
// Get related entities for tabs
|
|
3087
|
-
const tabDetailEntities = entity?.relatedEntities?.filter((re) => !re.hidden && re.
|
|
3088
|
-
const tabListEntities = entity?.relatedEntities?.filter((re) => !re.hidden && (!re.
|
|
3423
|
+
const tabDetailEntities = entity?.relatedEntities?.filter((re) => !re.hidden && re.layout?.type === 'tab-detail');
|
|
3424
|
+
const tabListEntities = entity?.relatedEntities?.filter((re) => !re.hidden && (!re.layout?.type || re.layout?.type === 'tab-list'));
|
|
3089
3425
|
// Build related tabs if dependencies are provided
|
|
3090
3426
|
const tabDetailTabs = await this.buildTabDetails(tabDetailEntities ?? [], dependencies);
|
|
3091
3427
|
const tabListTabs = await this.buildTabLists(tabListEntities ?? [], rootContext, dependencies);
|
|
3092
3428
|
// Build actions from single interface
|
|
3093
3429
|
const actions = this.buildActions(entity, singleInterface);
|
|
3094
|
-
|
|
3095
|
-
const evaluateExpressions = dependencies?.expressionEvaluator
|
|
3096
|
-
? this.createExpressionEvaluator(rootContext, dependencies.expressionEvaluator)
|
|
3097
|
-
: null;
|
|
3430
|
+
console.log({ rootContext, dependencies });
|
|
3098
3431
|
return {
|
|
3099
3432
|
id: entity?.name ?? '',
|
|
3100
|
-
title: singleInterface?.title ?? entity?.formats.individual ?? '',
|
|
3433
|
+
// title: singleInterface?.title ?? entity?.formats.individual ?? '',
|
|
3434
|
+
title: rootTitle ?? entity?.formats.individual,
|
|
3101
3435
|
label: entity?.formats.displayName ?? singleInterface?.title ?? '',
|
|
3102
3436
|
icon: entity?.icon,
|
|
3103
3437
|
actions: await this.buildEvaluatedActions(actions, evaluateExpressions),
|
|
3438
|
+
isPrimary: true,
|
|
3104
3439
|
settings: {
|
|
3105
3440
|
commands: {
|
|
3106
3441
|
reject: {
|
|
@@ -3183,7 +3518,7 @@ class AXPMainEntityContentBuilder {
|
|
|
3183
3518
|
};
|
|
3184
3519
|
}
|
|
3185
3520
|
},
|
|
3186
|
-
tabs: [...tabDetailTabs, ...tabListTabs],
|
|
3521
|
+
tabs: [...tabDetailTabs, ...tabListTabs, ...nestedTabListTabs],
|
|
3187
3522
|
content: [
|
|
3188
3523
|
{
|
|
3189
3524
|
type: 'grid-layout',
|
|
@@ -3197,7 +3532,7 @@ class AXPMainEntityContentBuilder {
|
|
|
3197
3532
|
},
|
|
3198
3533
|
},
|
|
3199
3534
|
},
|
|
3200
|
-
children:
|
|
3535
|
+
children: await Promise.all(combinedSections.map(async (s) => ({
|
|
3201
3536
|
type: 'grid-item-layout',
|
|
3202
3537
|
name: s.id,
|
|
3203
3538
|
options: {
|
|
@@ -3209,9 +3544,10 @@ class AXPMainEntityContentBuilder {
|
|
|
3209
3544
|
{
|
|
3210
3545
|
type: 'fieldset-layout',
|
|
3211
3546
|
options: {
|
|
3212
|
-
title:
|
|
3547
|
+
title: allGroups.find((g) => g.id === s.id)?.title ?? '',
|
|
3213
3548
|
collapsible: true,
|
|
3214
3549
|
isOpen: !s.collapsed,
|
|
3550
|
+
look: 'card'
|
|
3215
3551
|
},
|
|
3216
3552
|
children: [
|
|
3217
3553
|
{
|
|
@@ -3225,29 +3561,30 @@ class AXPMainEntityContentBuilder {
|
|
|
3225
3561
|
},
|
|
3226
3562
|
},
|
|
3227
3563
|
},
|
|
3228
|
-
children:
|
|
3229
|
-
|
|
3230
|
-
.
|
|
3231
|
-
const layout = getPropertyLayout(p.name);
|
|
3564
|
+
children: (await getVisiblePropertyByGroupId(s.id)).map((p) => {
|
|
3565
|
+
const layout = getPropertyLayout(p.name, p);
|
|
3566
|
+
const prefixed = p.__dataPath ? `${p.__dataPath}.${p.name}` : p.name;
|
|
3232
3567
|
return {
|
|
3233
3568
|
type: 'grid-item-layout',
|
|
3234
|
-
name:
|
|
3569
|
+
name: prefixed,
|
|
3235
3570
|
options: {
|
|
3236
3571
|
colSpan: layout?.positions?.lg?.colSpan,
|
|
3237
3572
|
colStart: layout?.positions?.lg?.colStart,
|
|
3238
3573
|
colEnd: layout?.positions?.lg?.colEnd,
|
|
3574
|
+
hidden: p.schema.hidden,
|
|
3239
3575
|
},
|
|
3240
3576
|
children: [
|
|
3241
3577
|
{
|
|
3242
3578
|
type: 'form-field',
|
|
3243
3579
|
options: {
|
|
3244
3580
|
label: p.title,
|
|
3581
|
+
showLabel: layout?.label?.visible ?? true,
|
|
3245
3582
|
},
|
|
3246
3583
|
children: [
|
|
3247
3584
|
{
|
|
3248
3585
|
type: p.schema.interface?.type ?? '',
|
|
3249
|
-
path:
|
|
3250
|
-
name:
|
|
3586
|
+
path: prefixed,
|
|
3587
|
+
name: prefixed,
|
|
3251
3588
|
defaultValue: p.schema.defaultValue,
|
|
3252
3589
|
children: p.schema.interface?.children,
|
|
3253
3590
|
triggers: p.schema.interface?.triggers,
|
|
@@ -3258,6 +3595,8 @@ class AXPMainEntityContentBuilder {
|
|
|
3258
3595
|
message: c.message,
|
|
3259
3596
|
options: c.options,
|
|
3260
3597
|
})),
|
|
3598
|
+
// Attach dataPath for merged properties to be available in widgets/options if needed
|
|
3599
|
+
dataPath: p.__dataPath,
|
|
3261
3600
|
}),
|
|
3262
3601
|
},
|
|
3263
3602
|
],
|
|
@@ -3269,7 +3608,7 @@ class AXPMainEntityContentBuilder {
|
|
|
3269
3608
|
],
|
|
3270
3609
|
},
|
|
3271
3610
|
],
|
|
3272
|
-
})),
|
|
3611
|
+
}))),
|
|
3273
3612
|
},
|
|
3274
3613
|
],
|
|
3275
3614
|
};
|
|
@@ -3283,7 +3622,8 @@ class AXPMainEntityContentBuilder {
|
|
|
3283
3622
|
const scope = {
|
|
3284
3623
|
context: {
|
|
3285
3624
|
eval: (path) => {
|
|
3286
|
-
|
|
3625
|
+
const value = get(context, path);
|
|
3626
|
+
return value;
|
|
3287
3627
|
},
|
|
3288
3628
|
},
|
|
3289
3629
|
};
|
|
@@ -3395,11 +3735,15 @@ class AXPLayoutAdapterFactory {
|
|
|
3395
3735
|
throw new Error(`Entity ${moduleName}.${entityName} not found`);
|
|
3396
3736
|
}
|
|
3397
3737
|
const rootContext = await this.loadRootContext(entity, id);
|
|
3738
|
+
// Build main and related pages
|
|
3739
|
+
const mainPage = await this.buildMainPage(entity, rootContext, dependencies);
|
|
3740
|
+
const relatedPages = await this.buildRelatedPages(entity, rootContext, dependencies);
|
|
3741
|
+
// Compose ordered pages around the primary page
|
|
3742
|
+
const orderedPages = this.composePagesWithPositions(mainPage, relatedPages, entity);
|
|
3398
3743
|
return this.layoutAdapterBuilder
|
|
3399
3744
|
.setEntity(entity, rootContext)
|
|
3400
3745
|
.setDependencies(dependencies)
|
|
3401
|
-
.
|
|
3402
|
-
.setRelatedPages(await this.buildRelatedPages(entity, rootContext, dependencies))
|
|
3746
|
+
.setPages(orderedPages)
|
|
3403
3747
|
.build();
|
|
3404
3748
|
}
|
|
3405
3749
|
async loadRootContext(entity, id) {
|
|
@@ -3407,31 +3751,107 @@ class AXPLayoutAdapterFactory {
|
|
|
3407
3751
|
return await fn(id);
|
|
3408
3752
|
}
|
|
3409
3753
|
async buildMainPage(entity, rootContext, dependencies) {
|
|
3410
|
-
return this.mainEntityContentBuilder.build(entity, rootContext, dependencies);
|
|
3754
|
+
return this.mainEntityContentBuilder.build(entity, rootContext, dependencies, await this.getRootTitle(entity, rootContext, dependencies));
|
|
3411
3755
|
}
|
|
3412
3756
|
async buildRelatedPages(entity, rootContext, dependencies) {
|
|
3413
3757
|
const pages = [];
|
|
3758
|
+
const rootTitle = await this.getRootTitle(entity, rootContext, dependencies);
|
|
3414
3759
|
// Page Details
|
|
3415
|
-
const pageDetailEntities = entity?.relatedEntities?.filter((re) => !re.hidden && re.
|
|
3760
|
+
const pageDetailEntities = entity?.relatedEntities?.filter((re) => !re.hidden && re.layout?.type === 'page-detail');
|
|
3416
3761
|
for (const relatedEntity of pageDetailEntities || []) {
|
|
3417
3762
|
const converter = this.relatedEntityConverterFactory.createPageDetailsConverter();
|
|
3418
|
-
pages.push(await converter.convert(relatedEntity, {
|
|
3763
|
+
pages.push(await converter.convert(relatedEntity, {
|
|
3764
|
+
...dependencies,
|
|
3765
|
+
context: rootContext,
|
|
3766
|
+
rootTitle: rootTitle,
|
|
3767
|
+
}));
|
|
3419
3768
|
}
|
|
3420
3769
|
// Page Lists
|
|
3421
|
-
const pageListEntities = entity?.relatedEntities?.filter((re) => !re.hidden && re.
|
|
3770
|
+
const pageListEntities = entity?.relatedEntities?.filter((re) => !re.hidden && re.layout?.type === 'page-list');
|
|
3422
3771
|
for (const relatedEntity of pageListEntities || []) {
|
|
3423
3772
|
const converter = this.relatedEntityConverterFactory.createPageListConverter();
|
|
3424
3773
|
pages.push(await converter.convert(relatedEntity, {
|
|
3425
3774
|
...dependencies,
|
|
3426
3775
|
context: rootContext,
|
|
3427
|
-
rootTitle:
|
|
3776
|
+
rootTitle: rootTitle,
|
|
3428
3777
|
}));
|
|
3429
3778
|
}
|
|
3779
|
+
// Include nested page-related entities from merge-detail related entities
|
|
3780
|
+
const mergeDetailEntities = entity?.relatedEntities?.filter((re) => !re.hidden && re.layout?.type === 'merge-detail');
|
|
3781
|
+
for (const mergeRelated of mergeDetailEntities || []) {
|
|
3782
|
+
try {
|
|
3783
|
+
const [moduleName, childEntityName] = (mergeRelated.entity || '').split('.');
|
|
3784
|
+
const childEntity = await dependencies.entityResolver.resolve(moduleName, childEntityName);
|
|
3785
|
+
if (!childEntity) {
|
|
3786
|
+
continue;
|
|
3787
|
+
}
|
|
3788
|
+
const nestedDataPath = mergeRelated?.persistence?.dataPath || childEntityName;
|
|
3789
|
+
const nestedContext = get(rootContext, nestedDataPath) ?? rootContext;
|
|
3790
|
+
const nestedPageDetailEntities = childEntity?.relatedEntities?.filter((re) => !re.hidden && re.layout?.type === 'page-detail');
|
|
3791
|
+
for (const nestedRelated of nestedPageDetailEntities || []) {
|
|
3792
|
+
const converter = this.relatedEntityConverterFactory.createPageDetailsConverter();
|
|
3793
|
+
pages.push(await converter.convert(nestedRelated, {
|
|
3794
|
+
...dependencies,
|
|
3795
|
+
context: nestedContext,
|
|
3796
|
+
rootTitle: rootTitle,
|
|
3797
|
+
}));
|
|
3798
|
+
}
|
|
3799
|
+
const nestedPageListEntities = childEntity?.relatedEntities?.filter((re) => !re.hidden && re.layout?.type === 'page-list');
|
|
3800
|
+
for (const nestedRelated of nestedPageListEntities || []) {
|
|
3801
|
+
const converter = this.relatedEntityConverterFactory.createPageListConverter();
|
|
3802
|
+
pages.push(await converter.convert(nestedRelated, {
|
|
3803
|
+
...dependencies,
|
|
3804
|
+
context: nestedContext,
|
|
3805
|
+
rootTitle: rootTitle,
|
|
3806
|
+
}));
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
catch {
|
|
3810
|
+
// Silently ignore failures for nested page inclusions
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3430
3813
|
return pages;
|
|
3431
3814
|
}
|
|
3815
|
+
composePagesWithPositions(mainPage, relatedPages, entity) {
|
|
3816
|
+
// Only consider related entities with layout types page-detail or page-list
|
|
3817
|
+
const pageEntities = (entity?.relatedEntities || []).filter((re) => !re.hidden && (re.layout?.type === 'page-detail' || re.layout?.type === 'page-list'));
|
|
3818
|
+
// Build a map from entity name to its layout config (order/position)
|
|
3819
|
+
const layoutConfigByEntity = {};
|
|
3820
|
+
for (const re of pageEntities) {
|
|
3821
|
+
const [, entityName] = (re.entity || '').split('.');
|
|
3822
|
+
const key = entityName || re.entity;
|
|
3823
|
+
layoutConfigByEntity[key] = { order: re.layout?.order, position: re.layout?.position };
|
|
3824
|
+
}
|
|
3825
|
+
// Split related pages into before/after buckets based on their relatedEntity layout
|
|
3826
|
+
const before = [];
|
|
3827
|
+
const after = [];
|
|
3828
|
+
for (const page of relatedPages) {
|
|
3829
|
+
const conf = layoutConfigByEntity[page.id];
|
|
3830
|
+
const position = conf?.position ?? 'after';
|
|
3831
|
+
if (position === 'before') {
|
|
3832
|
+
before.push(page);
|
|
3833
|
+
}
|
|
3834
|
+
else {
|
|
3835
|
+
after.push(page);
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
// Sort by order within each bucket (undefined orders go last)
|
|
3839
|
+
const sortByOrder = (a, b) => {
|
|
3840
|
+
const ao = layoutConfigByEntity[a.id]?.order ?? Number.POSITIVE_INFINITY;
|
|
3841
|
+
const bo = layoutConfigByEntity[b.id]?.order ?? Number.POSITIVE_INFINITY;
|
|
3842
|
+
return ao - bo;
|
|
3843
|
+
};
|
|
3844
|
+
before.sort(sortByOrder);
|
|
3845
|
+
after.sort(sortByOrder);
|
|
3846
|
+
// Ensure the main page is primary
|
|
3847
|
+
mainPage.isPrimary = true;
|
|
3848
|
+
// Compose final pages: before -> main -> after
|
|
3849
|
+
return [...before, mainPage, ...after];
|
|
3850
|
+
}
|
|
3432
3851
|
async getRootTitle(entity, rootContext, dependencies) {
|
|
3433
3852
|
// Logic for getting root title
|
|
3434
|
-
|
|
3853
|
+
const title = await dependencies.expressionEvaluator.evaluate(entity.interfaces?.master?.single?.title, rootContext);
|
|
3854
|
+
return title;
|
|
3435
3855
|
}
|
|
3436
3856
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: AXPLayoutAdapterFactory, deps: [{ token: AXPRelatedEntityConverterFactory }, { token: AXPMainEntityContentBuilder }, { token: AXPLayoutAdapterBuilder }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3437
3857
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: AXPLayoutAdapterFactory, providedIn: 'root' }); }
|
|
@@ -3499,6 +3919,81 @@ class AXPEntitySearchDefinitionProvider {
|
|
|
3499
3919
|
}
|
|
3500
3920
|
}
|
|
3501
3921
|
|
|
3922
|
+
//#region ---- Column Mapping Helpers ----
|
|
3923
|
+
/**
|
|
3924
|
+
* Maps an entity property to a list widget column configuration.
|
|
3925
|
+
* Preserves readonly behavior and leverages the property's interface options when available.
|
|
3926
|
+
*/
|
|
3927
|
+
function mapPropertyToWidgetColumn(property) {
|
|
3928
|
+
return {
|
|
3929
|
+
name: property.name,
|
|
3930
|
+
title: property.title,
|
|
3931
|
+
visible: true,
|
|
3932
|
+
widget: property.schema?.interface
|
|
3933
|
+
? {
|
|
3934
|
+
type: property.schema.interface.type || 'text-editor',
|
|
3935
|
+
path: property.name,
|
|
3936
|
+
options: {
|
|
3937
|
+
readonly: true,
|
|
3938
|
+
...property.schema.interface.options,
|
|
3939
|
+
},
|
|
3940
|
+
}
|
|
3941
|
+
: {
|
|
3942
|
+
type: 'text-editor',
|
|
3943
|
+
path: property.name,
|
|
3944
|
+
options: { readonly: true },
|
|
3945
|
+
},
|
|
3946
|
+
};
|
|
3947
|
+
}
|
|
3948
|
+
/**
|
|
3949
|
+
* Maps an entity column metadata (and its related property) to a list widget column configuration.
|
|
3950
|
+
*/
|
|
3951
|
+
function mapEntityColumnToWidgetColumn(entity, column) {
|
|
3952
|
+
const property = entity.properties.find((p) => p.name === column.name);
|
|
3953
|
+
return {
|
|
3954
|
+
name: column.name,
|
|
3955
|
+
title: column.title || property?.title || column.name,
|
|
3956
|
+
width: column.options?.width,
|
|
3957
|
+
visible: column.options?.visible !== false,
|
|
3958
|
+
widget: property?.schema?.interface
|
|
3959
|
+
? {
|
|
3960
|
+
type: property.schema.interface.type || 'text-editor',
|
|
3961
|
+
path: column.options?.dataPath || column.name,
|
|
3962
|
+
options: {
|
|
3963
|
+
readonly: true,
|
|
3964
|
+
...property.schema.interface.options,
|
|
3965
|
+
},
|
|
3966
|
+
}
|
|
3967
|
+
: {
|
|
3968
|
+
type: 'text-editor',
|
|
3969
|
+
path: column.options?.dataPath || column.name,
|
|
3970
|
+
options: { readonly: true },
|
|
3971
|
+
},
|
|
3972
|
+
};
|
|
3973
|
+
}
|
|
3974
|
+
//#endregion
|
|
3975
|
+
//#region ---- Include/Exclude Utilities ----
|
|
3976
|
+
/**
|
|
3977
|
+
* Applies include and exclude filters to a named collection while preserving order.
|
|
3978
|
+
*/
|
|
3979
|
+
function applyIncludeExclude(items, include = [], exclude = []) {
|
|
3980
|
+
let result = items;
|
|
3981
|
+
if (include.length > 0) {
|
|
3982
|
+
result = result.filter((i) => include.includes(i.name));
|
|
3983
|
+
}
|
|
3984
|
+
if (exclude.length > 0) {
|
|
3985
|
+
result = result.filter((i) => !exclude.includes(i.name));
|
|
3986
|
+
}
|
|
3987
|
+
return result;
|
|
3988
|
+
}
|
|
3989
|
+
/**
|
|
3990
|
+
* Returns only visible (non-hidden) properties of an entity.
|
|
3991
|
+
*/
|
|
3992
|
+
function getVisibleProperties(entity) {
|
|
3993
|
+
return entity.properties.filter((prop) => !prop.schema?.hidden);
|
|
3994
|
+
}
|
|
3995
|
+
//#endregion
|
|
3996
|
+
|
|
3502
3997
|
class AXPEntityListTableService {
|
|
3503
3998
|
constructor() {
|
|
3504
3999
|
//#region ---- Services & Dependencies ----
|
|
@@ -3506,6 +4001,9 @@ class AXPEntityListTableService {
|
|
|
3506
4001
|
this.workflow = inject(AXPWorkflowService);
|
|
3507
4002
|
this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
|
|
3508
4003
|
this.evaluateExpressions = async (options, data) => {
|
|
4004
|
+
if (!options) {
|
|
4005
|
+
return {};
|
|
4006
|
+
}
|
|
3509
4007
|
const scope = {
|
|
3510
4008
|
context: {
|
|
3511
4009
|
eval: (path) => {
|
|
@@ -3521,8 +4019,7 @@ class AXPEntityListTableService {
|
|
|
3521
4019
|
/**
|
|
3522
4020
|
* Convert Entity to List Widget Options
|
|
3523
4021
|
*/
|
|
3524
|
-
async convertEntityToListOptions(entity, options) {
|
|
3525
|
-
const allActions = entity.interfaces?.master?.list?.actions?.map((tr) => new AXPEntityCommandTriggerViewModel(entity, tr)) ?? [];
|
|
4022
|
+
async convertEntityToListOptions(entity, options, allActions) {
|
|
3526
4023
|
const listOptions = {
|
|
3527
4024
|
// 📊 Data Source
|
|
3528
4025
|
dataSource: this.createDataSource(entity),
|
|
@@ -3574,72 +4071,17 @@ class AXPEntityListTableService {
|
|
|
3574
4071
|
createColumnsFromProperties(entity, options) {
|
|
3575
4072
|
const excludeColumns = options?.excludeColumns || [];
|
|
3576
4073
|
const includeColumns = options?.includeColumns || [];
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
else {
|
|
3583
|
-
// Otherwise use properties
|
|
3584
|
-
columns = entity.properties
|
|
3585
|
-
.filter((prop) => !prop.schema.hidden) // Only visible properties
|
|
3586
|
-
.map((prop) => ({
|
|
3587
|
-
name: prop.name,
|
|
3588
|
-
title: prop.title,
|
|
3589
|
-
visible: true,
|
|
3590
|
-
widget: prop.schema.interface
|
|
3591
|
-
? {
|
|
3592
|
-
type: prop.schema.interface.type || 'text-editor',
|
|
3593
|
-
path: prop.name,
|
|
3594
|
-
options: {
|
|
3595
|
-
readonly: true,
|
|
3596
|
-
...prop.schema.interface.options,
|
|
3597
|
-
},
|
|
3598
|
-
}
|
|
3599
|
-
: {
|
|
3600
|
-
type: 'text-editor',
|
|
3601
|
-
path: prop.name,
|
|
3602
|
-
options: { readonly: true },
|
|
3603
|
-
},
|
|
3604
|
-
}));
|
|
3605
|
-
}
|
|
3606
|
-
// Apply include/exclude filters
|
|
3607
|
-
if (includeColumns.length > 0) {
|
|
3608
|
-
// If includeColumns is specified, only include those columns
|
|
3609
|
-
columns = columns.filter((col) => includeColumns.includes(col.name));
|
|
3610
|
-
}
|
|
3611
|
-
if (excludeColumns.length > 0) {
|
|
3612
|
-
// If excludeColumns is specified, exclude those columns
|
|
3613
|
-
columns = columns.filter((col) => !excludeColumns.includes(col.name));
|
|
3614
|
-
}
|
|
3615
|
-
return columns;
|
|
4074
|
+
// If columns are defined, use them; otherwise use visible properties
|
|
4075
|
+
const baseColumns = entity.columns && entity.columns.length > 0
|
|
4076
|
+
? entity.columns.map((col) => this.mapEntityColumnToWidgetColumn(entity, col))
|
|
4077
|
+
: getVisibleProperties(entity).map((prop) => mapPropertyToWidgetColumn(prop));
|
|
4078
|
+
return applyIncludeExclude(baseColumns, includeColumns, excludeColumns);
|
|
3616
4079
|
}
|
|
3617
4080
|
/**
|
|
3618
4081
|
* Map EntityTableColumn to ListWidgetColumn
|
|
3619
4082
|
*/
|
|
3620
4083
|
mapEntityColumnToWidgetColumn(entity, column) {
|
|
3621
|
-
|
|
3622
|
-
const property = entity.properties.find((p) => p.name === column.name);
|
|
3623
|
-
return {
|
|
3624
|
-
name: column.name,
|
|
3625
|
-
title: column.title || property?.title || column.name,
|
|
3626
|
-
width: column.options?.width,
|
|
3627
|
-
visible: column.options?.visible !== false,
|
|
3628
|
-
widget: property?.schema.interface
|
|
3629
|
-
? {
|
|
3630
|
-
type: property.schema.interface.type || 'text-editor',
|
|
3631
|
-
path: column.options?.dataPath || column.name,
|
|
3632
|
-
options: {
|
|
3633
|
-
readonly: true,
|
|
3634
|
-
...property.schema.interface.options,
|
|
3635
|
-
},
|
|
3636
|
-
}
|
|
3637
|
-
: {
|
|
3638
|
-
type: 'text-editor',
|
|
3639
|
-
path: column.options?.dataPath || column.name,
|
|
3640
|
-
options: { readonly: true },
|
|
3641
|
-
},
|
|
3642
|
-
};
|
|
4084
|
+
return mapEntityColumnToWidgetColumn(entity, column);
|
|
3643
4085
|
}
|
|
3644
4086
|
/**
|
|
3645
4087
|
* Convert Entity Actions to Row Commands
|
|
@@ -3655,7 +4097,7 @@ class AXPEntityListTableService {
|
|
|
3655
4097
|
icon: action.icon,
|
|
3656
4098
|
color: action.color,
|
|
3657
4099
|
look: 'outline',
|
|
3658
|
-
visible: action.hidden,
|
|
4100
|
+
visible: !action.hidden,
|
|
3659
4101
|
disabled: action.disabled,
|
|
3660
4102
|
}));
|
|
3661
4103
|
}
|
|
@@ -3666,6 +4108,36 @@ class AXPEntityListTableService {
|
|
|
3666
4108
|
const actions = entity.interfaces?.master?.list?.actions || [];
|
|
3667
4109
|
return actions.some((action) => action.scope === AXPEntityCommandScope.Selected);
|
|
3668
4110
|
}
|
|
4111
|
+
/**
|
|
4112
|
+
* Handle execution of a row command (shared by double-click and command handlers)
|
|
4113
|
+
*/
|
|
4114
|
+
async handleRowCommand(e, selectedRows, entity, allActions) {
|
|
4115
|
+
const data = e.data;
|
|
4116
|
+
const commandName = e.name;
|
|
4117
|
+
const action = allActions.find((c) => {
|
|
4118
|
+
return (c.name == e.name &&
|
|
4119
|
+
((selectedRows?.length
|
|
4120
|
+
? c.scope == AXPEntityCommandScope.Selected
|
|
4121
|
+
: c.scope == AXPEntityCommandScope.Individual) ||
|
|
4122
|
+
c.scope == AXPEntityCommandScope.TypeLevel));
|
|
4123
|
+
});
|
|
4124
|
+
const command = commandName.split('&')[0];
|
|
4125
|
+
const options = await this.evaluateExpressions(action?.options, data);
|
|
4126
|
+
await this.workflow.execute(command, {
|
|
4127
|
+
entity: getEntityInfo(entity).source,
|
|
4128
|
+
entityInfo: {
|
|
4129
|
+
name: entity.name,
|
|
4130
|
+
module: entity.module,
|
|
4131
|
+
title: entity.title,
|
|
4132
|
+
parentKey: entity.parentKey,
|
|
4133
|
+
source: entity.source,
|
|
4134
|
+
},
|
|
4135
|
+
data: action?.scope == AXPEntityCommandScope.Selected ? selectedRows : data,
|
|
4136
|
+
options: options,
|
|
4137
|
+
metadata: action?.metadata,
|
|
4138
|
+
});
|
|
4139
|
+
console.log('Entity List - Row command:', e.name, e.data);
|
|
4140
|
+
}
|
|
3669
4141
|
/**
|
|
3670
4142
|
* Create default events
|
|
3671
4143
|
*/
|
|
@@ -3674,38 +4146,27 @@ class AXPEntityListTableService {
|
|
|
3674
4146
|
onRowClick: (row) => {
|
|
3675
4147
|
console.log('Entity List - Row clicked:', row);
|
|
3676
4148
|
},
|
|
3677
|
-
onRowDoubleClick: (
|
|
3678
|
-
console.log('Entity List - Row double clicked:',
|
|
4149
|
+
onRowDoubleClick: (e) => {
|
|
4150
|
+
console.log('Entity List - Row double clicked:', e);
|
|
4151
|
+
const defaultAction = allActions.find((c) => {
|
|
4152
|
+
const commandName = c.name.split('&')[0];
|
|
4153
|
+
return (c.default || commandName === 'open-entity') && !c.hidden;
|
|
4154
|
+
});
|
|
4155
|
+
if (!defaultAction) {
|
|
4156
|
+
return;
|
|
4157
|
+
}
|
|
4158
|
+
const d = {
|
|
4159
|
+
component: e.component,
|
|
4160
|
+
name: defaultAction.name,
|
|
4161
|
+
data: e.data,
|
|
4162
|
+
};
|
|
4163
|
+
this.handleRowCommand(d, undefined, entity, allActions);
|
|
3679
4164
|
},
|
|
3680
4165
|
onSelectionChange: (selectedRows) => {
|
|
3681
4166
|
console.log('Entity List - Selection changed:', selectedRows);
|
|
3682
4167
|
},
|
|
3683
4168
|
onRowCommand: async (e, selectedRows) => {
|
|
3684
|
-
|
|
3685
|
-
const commandName = e.name;
|
|
3686
|
-
const action = allActions.find((c) => {
|
|
3687
|
-
return (c.name == e.name &&
|
|
3688
|
-
((selectedRows?.length
|
|
3689
|
-
? c.scope == AXPEntityCommandScope.Selected
|
|
3690
|
-
: c.scope == AXPEntityCommandScope.Individual) ||
|
|
3691
|
-
c.scope == AXPEntityCommandScope.TypeLevel));
|
|
3692
|
-
});
|
|
3693
|
-
const command = commandName.split('&')[0];
|
|
3694
|
-
const options = await this.evaluateExpressions(action?.options, data);
|
|
3695
|
-
await this.workflow.execute(command, {
|
|
3696
|
-
entity: getEntityInfo(entity).source,
|
|
3697
|
-
entityInfo: {
|
|
3698
|
-
name: entity.name,
|
|
3699
|
-
module: entity.module,
|
|
3700
|
-
title: entity.title,
|
|
3701
|
-
parentKey: entity.parentKey,
|
|
3702
|
-
source: entity.source,
|
|
3703
|
-
},
|
|
3704
|
-
data: action?.scope == AXPEntityCommandScope.Selected ? selectedRows : data,
|
|
3705
|
-
options: options,
|
|
3706
|
-
metadata: action?.metadata,
|
|
3707
|
-
});
|
|
3708
|
-
console.log('Entity List - Row command:', e.name, e.data);
|
|
4169
|
+
await this.handleRowCommand(e, selectedRows, entity, allActions);
|
|
3709
4170
|
},
|
|
3710
4171
|
};
|
|
3711
4172
|
}
|
|
@@ -3767,29 +4228,29 @@ class AXPEntityListToolbarService {
|
|
|
3767
4228
|
* Create Column Definitions for Toolbar
|
|
3768
4229
|
*/
|
|
3769
4230
|
createColumnDefinitions(entity, options) {
|
|
3770
|
-
const { columns = []
|
|
4231
|
+
const { columns = [] } = entity;
|
|
3771
4232
|
const excludeColumns = options?.excludeColumns || [];
|
|
3772
4233
|
const includeColumns = options?.includeColumns || [];
|
|
3773
|
-
const visibleProperties =
|
|
4234
|
+
const visibleProperties = getVisibleProperties(entity);
|
|
3774
4235
|
const visiblePropNames = new Set(visibleProperties.map(({ name }) => name));
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
name:
|
|
3789
|
-
title: property
|
|
3790
|
-
visible:
|
|
3791
|
-
};
|
|
3792
|
-
|
|
4236
|
+
// Prefer explicit entity.columns if present; fallback to visible properties
|
|
4237
|
+
const baseColumns = columns.length > 0
|
|
4238
|
+
? columns
|
|
4239
|
+
.filter(({ name }) => visiblePropNames.has(name))
|
|
4240
|
+
.map((column) => {
|
|
4241
|
+
const property = visibleProperties.find(({ name }) => name === column.name);
|
|
4242
|
+
return {
|
|
4243
|
+
name: column.name,
|
|
4244
|
+
title: property?.title,
|
|
4245
|
+
visible: column?.options?.visible ?? true,
|
|
4246
|
+
};
|
|
4247
|
+
})
|
|
4248
|
+
: visibleProperties.map((property) => ({
|
|
4249
|
+
name: property.name,
|
|
4250
|
+
title: property.title,
|
|
4251
|
+
visible: true,
|
|
4252
|
+
}));
|
|
4253
|
+
return applyIncludeExclude(baseColumns, includeColumns, excludeColumns);
|
|
3793
4254
|
}
|
|
3794
4255
|
/**
|
|
3795
4256
|
* Create Sort Definitions for Toolbar
|
|
@@ -3818,7 +4279,6 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
|
|
|
3818
4279
|
this.entityListTableService = inject(AXPEntityListTableService);
|
|
3819
4280
|
this.entityListToolbarService = inject(AXPEntityListToolbarService);
|
|
3820
4281
|
this.layoutThemeService = inject(AXPLayoutThemeService);
|
|
3821
|
-
this.container = viewChild(AXPWidgetContainerComponent, ...(ngDevMode ? [{ debugName: "container" }] : []));
|
|
3822
4282
|
this.isMounted = signal(false, ...(ngDevMode ? [{ debugName: "isMounted" }] : []));
|
|
3823
4283
|
this.entity = signal(null, ...(ngDevMode ? [{ debugName: "entity" }] : []));
|
|
3824
4284
|
this.listNode = signal(null, ...(ngDevMode ? [{ debugName: "listNode" }] : []));
|
|
@@ -3826,7 +4286,7 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
|
|
|
3826
4286
|
this.allWidgets = viewChildren(AXPWidgetRendererDirective, ...(ngDevMode ? [{ debugName: "allWidgets" }] : []));
|
|
3827
4287
|
this.listWidget = linkedSignal(() => this.allWidgets().find((widget) => widget.node()?.type === AXPWidgetsCatalog.list));
|
|
3828
4288
|
this.toolbarWidget = computed(() => this.allWidgets().find((widget) => widget.node()?.type === AXPWidgetsCatalog.listToolbar), ...(ngDevMode ? [{ debugName: "toolbarWidget" }] : []));
|
|
3829
|
-
this.selectedItems =
|
|
4289
|
+
this.selectedItems = signal([], ...(ngDevMode ? [{ debugName: "selectedItems" }] : []));
|
|
3830
4290
|
this.toolbarNode = signal(null, ...(ngDevMode ? [{ debugName: "toolbarNode" }] : []));
|
|
3831
4291
|
this.destroyed = new Subject();
|
|
3832
4292
|
//options
|
|
@@ -3839,7 +4299,7 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
|
|
|
3839
4299
|
//actions
|
|
3840
4300
|
this.allActions = computed(() => {
|
|
3841
4301
|
const originalList = this.entity()?.interfaces?.master?.list?.actions ?? [];
|
|
3842
|
-
const externalActionList =
|
|
4302
|
+
const externalActionList = this.externalActions() ?? [];
|
|
3843
4303
|
const usedOverrideActions = new Set();
|
|
3844
4304
|
const mergedActions = originalList.map((originalAction) => {
|
|
3845
4305
|
const originalCommandName = typeof originalAction.command === 'string' ? originalAction.command : originalAction.command?.name;
|
|
@@ -3948,7 +4408,9 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
|
|
|
3948
4408
|
parentKey: this.entity()?.parentKey,
|
|
3949
4409
|
source: this.entity()?.source,
|
|
3950
4410
|
},
|
|
3951
|
-
data: action?.scope == AXPEntityCommandScope.Selected
|
|
4411
|
+
data: action?.scope == AXPEntityCommandScope.Selected
|
|
4412
|
+
? this.selectedItems()
|
|
4413
|
+
: action?.options?.['process']?.data || null,
|
|
3952
4414
|
options: action?.options,
|
|
3953
4415
|
metadata: action?.metadata,
|
|
3954
4416
|
});
|
|
@@ -4014,10 +4476,30 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
|
|
|
4014
4476
|
* TODO: Implement column change logic
|
|
4015
4477
|
*/
|
|
4016
4478
|
handleColumnChanges(changeTracker) {
|
|
4017
|
-
if (changeTracker.isColumnsChanged) {
|
|
4018
|
-
|
|
4019
|
-
console.log('Column changes detected - implementation needed');
|
|
4479
|
+
if (!changeTracker.isColumnsChanged) {
|
|
4480
|
+
return;
|
|
4020
4481
|
}
|
|
4482
|
+
const listInstance = this.listWidget()?.instance;
|
|
4483
|
+
const toolbarState = this.getValue()?.toolbar;
|
|
4484
|
+
if (!listInstance || !toolbarState?.columns) {
|
|
4485
|
+
return;
|
|
4486
|
+
}
|
|
4487
|
+
// Current columns from list options
|
|
4488
|
+
const currentColumns = (listInstance.options()['columns'] || []);
|
|
4489
|
+
const columnByName = new Map(currentColumns.map((c) => [c.name, c]));
|
|
4490
|
+
// Build new ordered columns array based on toolbar order/visibility
|
|
4491
|
+
const updatedColumns = toolbarState.columns
|
|
4492
|
+
.map((q) => {
|
|
4493
|
+
const base = columnByName.get(q.name);
|
|
4494
|
+
if (!base) {
|
|
4495
|
+
return null;
|
|
4496
|
+
}
|
|
4497
|
+
// Preserve all existing column config but update visibility
|
|
4498
|
+
return { ...base, visible: q.visible };
|
|
4499
|
+
})
|
|
4500
|
+
.filter((c) => c != null);
|
|
4501
|
+
// Apply updated columns to the list widget and refresh
|
|
4502
|
+
listInstance.setOptions({ columns: updatedColumns });
|
|
4021
4503
|
}
|
|
4022
4504
|
async ngOnInit() {
|
|
4023
4505
|
super.ngOnInit();
|
|
@@ -4035,7 +4517,7 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
|
|
|
4035
4517
|
excludeColumns: this.excludeColumns(),
|
|
4036
4518
|
includeColumns: this.includeColumns(),
|
|
4037
4519
|
};
|
|
4038
|
-
const listOptions = await this.entityListTableService.convertEntityToListOptions(resolvedEntity, options);
|
|
4520
|
+
const listOptions = await this.entityListTableService.convertEntityToListOptions(resolvedEntity, options, this.allActions());
|
|
4039
4521
|
const toolbarOptions = await this.entityListToolbarService.convertEntityToolbarOptions(resolvedEntity, options);
|
|
4040
4522
|
this.listNode.set({
|
|
4041
4523
|
type: AXPWidgetsCatalog.list,
|
|
@@ -4057,7 +4539,7 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
|
|
|
4057
4539
|
},
|
|
4058
4540
|
});
|
|
4059
4541
|
}
|
|
4060
|
-
ngAfterViewInit() {
|
|
4542
|
+
async ngAfterViewInit() {
|
|
4061
4543
|
this.workflow.events$
|
|
4062
4544
|
.pipe(ofType(AXPRefreshEvent))
|
|
4063
4545
|
.pipe(takeUntil(this.destroyed))
|
|
@@ -4066,6 +4548,15 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
|
|
|
4066
4548
|
this.listWidget()?.instance.call('refresh');
|
|
4067
4549
|
}
|
|
4068
4550
|
});
|
|
4551
|
+
const listWidget = (await this.layoutService.waitForWidget(`${this.entitySource()}-tab-list_table`, 500));
|
|
4552
|
+
if (listWidget?.api && typeof listWidget.api === 'function') {
|
|
4553
|
+
const onSelectionChange = listWidget.api()['onSelectionChange'];
|
|
4554
|
+
if (onSelectionChange) {
|
|
4555
|
+
onSelectionChange.pipe(takeUntil(this.destroyed)).subscribe((e) => {
|
|
4556
|
+
this.selectedItems.set(e);
|
|
4557
|
+
});
|
|
4558
|
+
}
|
|
4559
|
+
}
|
|
4069
4560
|
}
|
|
4070
4561
|
ngOnDestroy() {
|
|
4071
4562
|
this.listWidget.set(undefined);
|
|
@@ -4073,7 +4564,7 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
|
|
|
4073
4564
|
this.destroyed.complete();
|
|
4074
4565
|
}
|
|
4075
4566
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: AXPEntityListWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
4076
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: AXPEntityListWidgetViewComponent, isStandalone: true, selector: "ng-component", host: { properties: { "class": "\"ax-h-full\"" } }, providers: [AXPEntityListTableService, AXPEntityListToolbarService], viewQueries: [{ propertyName: "
|
|
4567
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: AXPEntityListWidgetViewComponent, isStandalone: true, selector: "ng-component", host: { properties: { "class": "\"ax-h-full\"" } }, providers: [AXPEntityListTableService, AXPEntityListToolbarService], viewQueries: [{ propertyName: "list", first: true, predicate: ["list"], descendants: true, isSignal: true }, { propertyName: "allWidgets", predicate: AXPWidgetRendererDirective, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
|
|
4077
4568
|
@if (showEntityActions()) {
|
|
4078
4569
|
<div class="ax-flex ax-gap-2 ax-justify-end ax-mb-4">
|
|
4079
4570
|
@for (action of primaryActions(); track $index) {
|
|
@@ -4804,6 +5295,7 @@ class AXPLookupWidgetSelectorViewModel {
|
|
|
4804
5295
|
this.options = options;
|
|
4805
5296
|
this.workflow = this.injector.get(AXPWorkflowService);
|
|
4806
5297
|
this.filterOperatorMiddleware = this.injector.get(AXPFilterOperatorMiddlewareService);
|
|
5298
|
+
this.widgetResolver = this.injector.get(AXPWidgetRegistryService);
|
|
4807
5299
|
this.dataSource = new AXDataSource({
|
|
4808
5300
|
byKey: (key) => {
|
|
4809
5301
|
const func = this.entityDef.queries.byKey.execute;
|
|
@@ -4833,15 +5325,33 @@ class AXPLookupWidgetSelectorViewModel {
|
|
|
4833
5325
|
return this.inlineFiltersPlaceholders().length > 0;
|
|
4834
5326
|
}, ...(ngDevMode ? [{ debugName: "hasInlineFilters" }] : []));
|
|
4835
5327
|
this.columns = () => {
|
|
4836
|
-
const
|
|
4837
|
-
const
|
|
4838
|
-
const
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
5328
|
+
const { columns = [], properties } = this.entityDef;
|
|
5329
|
+
const visibleProperties = properties.filter(({ schema }) => !schema?.hidden);
|
|
5330
|
+
const visiblePropNames = new Set(visibleProperties.map(({ name }) => name));
|
|
5331
|
+
return columns
|
|
5332
|
+
.filter(({ name, showAs }) => visiblePropNames.has(name) || showAs)
|
|
5333
|
+
.filter(({ name }) => ((this.options.columns?.length ?? 0) == 0) || this.options.columns?.includes(name))
|
|
5334
|
+
.map((column) => {
|
|
5335
|
+
if (column.showAs) {
|
|
5336
|
+
const widgetConfig = this.widgetResolver.resolve(column.showAs.type);
|
|
5337
|
+
const property = {
|
|
5338
|
+
...widgetConfig,
|
|
5339
|
+
name: column.name,
|
|
5340
|
+
title: column.title ?? '',
|
|
5341
|
+
schema: {
|
|
5342
|
+
dataType: 'string',
|
|
5343
|
+
interface: {
|
|
5344
|
+
type: column.showAs.type,
|
|
5345
|
+
options: column.showAs.options,
|
|
5346
|
+
},
|
|
5347
|
+
},
|
|
5348
|
+
};
|
|
5349
|
+
return new AXPEntityListViewColumnViewModel(property, column);
|
|
5350
|
+
}
|
|
5351
|
+
else {
|
|
5352
|
+
const property = visibleProperties.find(({ name }) => name === column.name);
|
|
5353
|
+
return new AXPEntityListViewColumnViewModel(property, column);
|
|
5354
|
+
}
|
|
4845
5355
|
});
|
|
4846
5356
|
};
|
|
4847
5357
|
this.inlineFilters = {
|
|
@@ -4950,13 +5460,24 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
|
|
|
4950
5460
|
this.entity = computed(() => this.options()['entity'], ...(ngDevMode ? [{ debugName: "entity" }] : []));
|
|
4951
5461
|
this.disabled = computed(() => this.options()['disabled'], ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
4952
5462
|
this.columns = computed(() => this.options()['columns'] ?? [], ...(ngDevMode ? [{ debugName: "columns" }] : []));
|
|
5463
|
+
this.textField = computed(() => this.options()['textField'] ?? '', ...(ngDevMode ? [{ debugName: "textField" }] : []));
|
|
4953
5464
|
this.customFilter = computed(() => this.options()['filter'], ...(ngDevMode ? [{ debugName: "customFilter" }] : []));
|
|
4954
5465
|
this.multiple = computed(() => (this.options()['multiple'] ?? false), ...(ngDevMode ? [{ debugName: "multiple" }] : []));
|
|
4955
5466
|
this.look = computed(() => this.options()['look'] ?? 'lookup', ...(ngDevMode ? [{ debugName: "look" }] : []));
|
|
4956
5467
|
this.allowClear = computed(() => (this.options()['allowClear'] ?? false), ...(ngDevMode ? [{ debugName: "allowClear" }] : []));
|
|
4957
|
-
this.
|
|
4958
|
-
|
|
4959
|
-
|
|
5468
|
+
this.defaultTextField = computed(() => {
|
|
5469
|
+
const textField = this.entityDef()?.formats.lookup ?? this.entityDef()?.properties.find((c) => c.name != 'id')?.name ?? 'title';
|
|
5470
|
+
return textField;
|
|
5471
|
+
}, ...(ngDevMode ? [{ debugName: "defaultTextField" }] : []));
|
|
5472
|
+
this.displayField = computed(() => {
|
|
5473
|
+
if (this.textField()) {
|
|
5474
|
+
return this.textField();
|
|
5475
|
+
}
|
|
5476
|
+
return this.defaultTextField();
|
|
5477
|
+
}, ...(ngDevMode ? [{ debugName: "displayField" }] : []));
|
|
5478
|
+
this.selectedItemsText = computed(() => {
|
|
5479
|
+
return this.selectedItems().map((item) => get(item, this.displayField())).join(', ');
|
|
5480
|
+
}, ...(ngDevMode ? [{ debugName: "selectedItemsText" }] : []));
|
|
4960
5481
|
this.valueField = computed(() => this.entityDef()?.properties.find((c) => c.name == 'id')?.name ?? 'id', ...(ngDevMode ? [{ debugName: "valueField" }] : []));
|
|
4961
5482
|
this.entityDef = signal(null, ...(ngDevMode ? [{ debugName: "entityDef" }] : []));
|
|
4962
5483
|
this.searchTerm = signal(null, ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
|
|
@@ -5096,6 +5617,7 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
|
|
|
5096
5617
|
this.selectedItems.set(items);
|
|
5097
5618
|
//
|
|
5098
5619
|
const keys = items.map((item) => get(item, this.valueField()));
|
|
5620
|
+
const text = items.map((item) => get(item, this.displayField()));
|
|
5099
5621
|
//
|
|
5100
5622
|
// extract data from valueField and set context by expose path
|
|
5101
5623
|
if (this.expose()) {
|
|
@@ -5147,11 +5669,12 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
|
|
|
5147
5669
|
}
|
|
5148
5670
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: AXPLookupWidgetEditComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
5149
5671
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: AXPLookupWidgetEditComponent, isStandalone: true, selector: "axp-lookup-widget-edit", viewQueries: [{ propertyName: "textbox", first: true, predicate: AXTagBoxComponent, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
|
|
5672
|
+
@if(vm()) {
|
|
5150
5673
|
@if (look() == 'select') {
|
|
5151
5674
|
<ax-select-box
|
|
5152
5675
|
[dataSource]="vm()?.dataSource!"
|
|
5153
5676
|
[ngModel]="selectedItems()"
|
|
5154
|
-
[textField]="
|
|
5677
|
+
[textField]="displayField()"
|
|
5155
5678
|
[valueField]="valueField()"
|
|
5156
5679
|
[disabled]="disabled()"
|
|
5157
5680
|
[multiple]="multiple()"
|
|
@@ -5167,7 +5690,7 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
|
|
|
5167
5690
|
} @else {
|
|
5168
5691
|
<ax-tag-box
|
|
5169
5692
|
[ngModel]="selectedItems()"
|
|
5170
|
-
[textField]="
|
|
5693
|
+
[textField]="displayField()"
|
|
5171
5694
|
[valueField]="valueField()"
|
|
5172
5695
|
(onValueChanged)="handleValueChange($event)"
|
|
5173
5696
|
[placeholder]="placeholder() | translate | async"
|
|
@@ -5204,6 +5727,7 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
|
|
|
5204
5727
|
</ax-suffix>
|
|
5205
5728
|
</ax-tag-box>
|
|
5206
5729
|
}
|
|
5730
|
+
}
|
|
5207
5731
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type:
|
|
5208
5732
|
//
|
|
5209
5733
|
AXButtonModule }, { kind: "component", type: i3$1.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: AXDecoratorModule }, { kind: "component", type: i3.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i3.AXDecoratorClearButtonComponent, selector: "ax-clear-button", inputs: ["icon"] }, { kind: "component", type: i3.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i1.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXValidationModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "directive", type: i5$1.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "ngmodule", type: AXTagBoxModule }, { kind: "component", type: i6.AXTagBoxComponent, selector: "ax-tag-box", inputs: ["disabled", "tabIndex", "readonly", "value", "state", "name", "id", "placeholder", "allowNull", "type", "look", "addOnComma", "addOnEnter", "valueField", "textField", "readonlyField", "allowDuplicateValues"], outputs: ["onBlur", "onFocus", "valueChange", "stateChange", "onValueChanged", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXSelectBoxModule }, { kind: "component", type: i7$1.AXSelectBoxComponent, selector: "ax-select-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "minValue", "maxValue", "value", "state", "name", "id", "type", "look", "multiple", "valueField", "textField", "disabledField", "textTemplate", "selectedItems", "isItemTruncated", "showItemTooltip", "dataSource", "minRecordsForSearch", "caption", "itemTemplate", "selectedTemplate", "emptyTemplate", "loadingTemplate", "dropdownWidth", "searchBoxAutoFocus"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onOpened", "onClosed", "onItemSelected", "onItemClick"] }, { kind: "component", type: AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "pipe", type: i7.AsyncPipe, name: "async" }, { kind: "pipe", type: i8.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
@@ -5213,11 +5737,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImpor
|
|
|
5213
5737
|
args: [{
|
|
5214
5738
|
selector: 'axp-lookup-widget-edit',
|
|
5215
5739
|
template: `
|
|
5740
|
+
@if(vm()) {
|
|
5216
5741
|
@if (look() == 'select') {
|
|
5217
5742
|
<ax-select-box
|
|
5218
5743
|
[dataSource]="vm()?.dataSource!"
|
|
5219
5744
|
[ngModel]="selectedItems()"
|
|
5220
|
-
[textField]="
|
|
5745
|
+
[textField]="displayField()"
|
|
5221
5746
|
[valueField]="valueField()"
|
|
5222
5747
|
[disabled]="disabled()"
|
|
5223
5748
|
[multiple]="multiple()"
|
|
@@ -5233,7 +5758,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImpor
|
|
|
5233
5758
|
} @else {
|
|
5234
5759
|
<ax-tag-box
|
|
5235
5760
|
[ngModel]="selectedItems()"
|
|
5236
|
-
[textField]="
|
|
5761
|
+
[textField]="displayField()"
|
|
5237
5762
|
[valueField]="valueField()"
|
|
5238
5763
|
(onValueChanged)="handleValueChange($event)"
|
|
5239
5764
|
[placeholder]="placeholder() | translate | async"
|
|
@@ -5270,6 +5795,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImpor
|
|
|
5270
5795
|
</ax-suffix>
|
|
5271
5796
|
</ax-tag-box>
|
|
5272
5797
|
}
|
|
5798
|
+
}
|
|
5273
5799
|
`,
|
|
5274
5800
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
5275
5801
|
imports: [
|
|
@@ -6155,7 +6681,7 @@ class AXPShowDetailViewAction extends AXPWorkflowAction {
|
|
|
6155
6681
|
const [module, entity] = context.getVariable('entity').split('.');
|
|
6156
6682
|
const { id } = context.getVariable('data');
|
|
6157
6683
|
const newPayload = {
|
|
6158
|
-
commands: `/${this.sessionService.application?.name}/m/${module}/e/${entity}/${id}/view`,
|
|
6684
|
+
commands: `/${this.sessionService.application?.name}/m/${module}/e/${entity}/${id}/new-view`,
|
|
6159
6685
|
};
|
|
6160
6686
|
context.setVariable('payload', newPayload);
|
|
6161
6687
|
this.navigation.execute(context);
|
|
@@ -6616,7 +7142,7 @@ function entityDetailsSimpleCondition(fk) {
|
|
|
6616
7142
|
value: '{{context.eval("id")}}',
|
|
6617
7143
|
};
|
|
6618
7144
|
}
|
|
6619
|
-
function entityDetailsReferenceCondition() {
|
|
7145
|
+
function entityDetailsReferenceCondition(type) {
|
|
6620
7146
|
return [
|
|
6621
7147
|
{
|
|
6622
7148
|
name: 'reference.id',
|
|
@@ -6626,7 +7152,7 @@ function entityDetailsReferenceCondition() {
|
|
|
6626
7152
|
{
|
|
6627
7153
|
name: 'reference.type',
|
|
6628
7154
|
operator: { type: 'equal' },
|
|
6629
|
-
value:
|
|
7155
|
+
value: type,
|
|
6630
7156
|
},
|
|
6631
7157
|
];
|
|
6632
7158
|
}
|
|
@@ -6636,6 +7162,7 @@ function entityDetailsEditAction() {
|
|
|
6636
7162
|
command: 'quick-modify-entity',
|
|
6637
7163
|
priority: 'secondary',
|
|
6638
7164
|
type: 'update',
|
|
7165
|
+
default: true,
|
|
6639
7166
|
scope: AXPEntityCommandScope.Individual,
|
|
6640
7167
|
};
|
|
6641
7168
|
}
|
|
@@ -6652,7 +7179,7 @@ function entityOverrideDetailsViewAction() {
|
|
|
6652
7179
|
function entityDetailsCrudActions(parentId) {
|
|
6653
7180
|
return [entityDetailsCreateActions(parentId), entityDetailsEditAction(), entityOverrideDetailsViewAction()];
|
|
6654
7181
|
}
|
|
6655
|
-
function entityDetailsReferenceCreateActions() {
|
|
7182
|
+
function entityDetailsReferenceCreateActions(type) {
|
|
6656
7183
|
return [
|
|
6657
7184
|
{
|
|
6658
7185
|
title: '@general:actions.create.title',
|
|
@@ -6665,7 +7192,7 @@ function entityDetailsReferenceCreateActions() {
|
|
|
6665
7192
|
data: {
|
|
6666
7193
|
reference: {
|
|
6667
7194
|
id: '{{context.eval("id")}}',
|
|
6668
|
-
type:
|
|
7195
|
+
type: type,
|
|
6669
7196
|
},
|
|
6670
7197
|
},
|
|
6671
7198
|
},
|