@meshmakers/octo-meshboard 3.3.490 → 3.3.510
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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { firstValueFrom, from, map, Observable, of, catchError, interval } from 'rxjs';
|
|
2
|
-
import { FieldFilterOperatorsDto, CkTypeSelectorService, DeleteStrategiesDto, AssociationModOptionsDto, CkModelService, AttributeSelectorService, GraphQL, GetCkTypeAvailableQueryColumnsDtoGQL, SortOrdersDto, HealthService, TENANT_ID_PROVIDER, AssetRepoService, JobManagementService } from '@meshmakers/octo-services';
|
|
2
|
+
import { FieldFilterOperatorsDto, CkTypeSelectorService, DeleteStrategiesDto, AssociationModOptionsDto, CkModelService, GraphDirectionDto, AttributeSelectorService, GraphQL, GetCkTypeAvailableQueryColumnsDtoGQL, SortOrdersDto, HealthService, TENANT_ID_PROVIDER, AssetRepoService, JobManagementService } from '@meshmakers/octo-services';
|
|
3
3
|
export { TENANT_ID_PROVIDER } from '@meshmakers/octo-services';
|
|
4
4
|
import * as i0 from '@angular/core';
|
|
5
5
|
import { Injectable, inject, EventEmitter, forwardRef, Output, Input, ViewChild, Component, Injector, EnvironmentInjector, ApplicationRef, signal, computed, Directive, makeEnvironmentProviders, provideAppInitializer, InjectionToken, effect } from '@angular/core';
|
|
@@ -2586,6 +2586,84 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
|
|
|
2586
2586
|
}]
|
|
2587
2587
|
}], ctorParameters: () => [{ type: i1.Apollo }] });
|
|
2588
2588
|
|
|
2589
|
+
const GetAssociationTargetsDocumentDto = gql `
|
|
2590
|
+
query getAssociationTargets($rtId: OctoObjectId!, $ckTypeId: String!, $targetCkTypeId: String!, $roleId: String!, $direction: GraphDirection!, $first: Int, $attributeNames: [String]) {
|
|
2591
|
+
runtime {
|
|
2592
|
+
runtimeEntities(rtId: $rtId, ckId: $ckTypeId, first: 1) {
|
|
2593
|
+
items {
|
|
2594
|
+
associations {
|
|
2595
|
+
targets(
|
|
2596
|
+
ckId: $targetCkTypeId
|
|
2597
|
+
roleId: $roleId
|
|
2598
|
+
direction: $direction
|
|
2599
|
+
first: $first
|
|
2600
|
+
) {
|
|
2601
|
+
totalCount
|
|
2602
|
+
items {
|
|
2603
|
+
rtId
|
|
2604
|
+
ckTypeId
|
|
2605
|
+
rtWellKnownName
|
|
2606
|
+
attributes(attributeNames: $attributeNames, resolveEnumValuesToNames: true) {
|
|
2607
|
+
items {
|
|
2608
|
+
attributeName
|
|
2609
|
+
value
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
`;
|
|
2620
|
+
class GetAssociationTargetsDtoGQL extends i1.Query {
|
|
2621
|
+
document = GetAssociationTargetsDocumentDto;
|
|
2622
|
+
constructor(apollo) {
|
|
2623
|
+
super(apollo);
|
|
2624
|
+
}
|
|
2625
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: GetAssociationTargetsDtoGQL, deps: [{ token: i1.Apollo }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2626
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: GetAssociationTargetsDtoGQL, providedIn: 'root' });
|
|
2627
|
+
}
|
|
2628
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: GetAssociationTargetsDtoGQL, decorators: [{
|
|
2629
|
+
type: Injectable,
|
|
2630
|
+
args: [{
|
|
2631
|
+
providedIn: 'root'
|
|
2632
|
+
}]
|
|
2633
|
+
}], ctorParameters: () => [{ type: i1.Apollo }] });
|
|
2634
|
+
|
|
2635
|
+
const GetCkTypeAttributesForMeshboardDocumentDto = gql `
|
|
2636
|
+
query getCkTypeAttributesForMeshboard($ckTypeId: String!, $first: Int) {
|
|
2637
|
+
constructionKit {
|
|
2638
|
+
types(rtCkId: $ckTypeId, first: 1) {
|
|
2639
|
+
items {
|
|
2640
|
+
rtCkTypeId
|
|
2641
|
+
attributes(first: $first) {
|
|
2642
|
+
items {
|
|
2643
|
+
attributeName
|
|
2644
|
+
attributeValueType
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
`;
|
|
2652
|
+
class GetCkTypeAttributesForMeshboardDtoGQL extends i1.Query {
|
|
2653
|
+
document = GetCkTypeAttributesForMeshboardDocumentDto;
|
|
2654
|
+
constructor(apollo) {
|
|
2655
|
+
super(apollo);
|
|
2656
|
+
}
|
|
2657
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: GetCkTypeAttributesForMeshboardDtoGQL, deps: [{ token: i1.Apollo }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2658
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: GetCkTypeAttributesForMeshboardDtoGQL, providedIn: 'root' });
|
|
2659
|
+
}
|
|
2660
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: GetCkTypeAttributesForMeshboardDtoGQL, decorators: [{
|
|
2661
|
+
type: Injectable,
|
|
2662
|
+
args: [{
|
|
2663
|
+
providedIn: 'root'
|
|
2664
|
+
}]
|
|
2665
|
+
}], ctorParameters: () => [{ type: i1.Apollo }] });
|
|
2666
|
+
|
|
2589
2667
|
/**
|
|
2590
2668
|
* Service for resolving MeshBoard variables in filter values.
|
|
2591
2669
|
* Variables use the syntax $variableName and are replaced at query execution time.
|
|
@@ -2734,6 +2812,8 @@ class MeshBoardDataService {
|
|
|
2734
2812
|
getCkModelsWithStateGQL = inject(GetCkModelsWithStateDtoGQL);
|
|
2735
2813
|
getEntitiesByCkTypeGQL = inject(GetEntitiesByCkTypeDtoGQL);
|
|
2736
2814
|
executeRuntimeQueryGQL = inject(ExecuteRuntimeQueryDtoGQL);
|
|
2815
|
+
getAssociationTargetsGQL = inject(GetAssociationTargetsDtoGQL);
|
|
2816
|
+
getCkTypeAttributesGQL = inject(GetCkTypeAttributesForMeshboardDtoGQL);
|
|
2737
2817
|
apollo = inject(Apollo);
|
|
2738
2818
|
stateService = inject(MeshBoardStateService);
|
|
2739
2819
|
variableService = inject(MeshBoardVariableService);
|
|
@@ -3050,6 +3130,63 @@ class MeshBoardDataService {
|
|
|
3050
3130
|
}
|
|
3051
3131
|
}
|
|
3052
3132
|
// ============================================================================
|
|
3133
|
+
// Association Target Queries
|
|
3134
|
+
// ============================================================================
|
|
3135
|
+
/**
|
|
3136
|
+
* Fetches target entities of an association group with their attributes.
|
|
3137
|
+
* Uses the associations.targets() GraphQL endpoint.
|
|
3138
|
+
*/
|
|
3139
|
+
fetchAssociationTargets(sourceRtId, sourceCkTypeId, targetCkTypeId, roleId, direction, attributeNames, first) {
|
|
3140
|
+
const graphDirection = direction === 'out' ? GraphDirectionDto.OutboundDto : GraphDirectionDto.InboundDto;
|
|
3141
|
+
return this.getAssociationTargetsGQL.fetch({
|
|
3142
|
+
variables: {
|
|
3143
|
+
rtId: sourceRtId,
|
|
3144
|
+
ckTypeId: sourceCkTypeId,
|
|
3145
|
+
targetCkTypeId,
|
|
3146
|
+
roleId,
|
|
3147
|
+
direction: graphDirection,
|
|
3148
|
+
first: first ?? 100,
|
|
3149
|
+
attributeNames: attributeNames ?? null
|
|
3150
|
+
}
|
|
3151
|
+
}).pipe(map(result => {
|
|
3152
|
+
const entity = result.data?.runtime?.runtimeEntities?.items?.[0];
|
|
3153
|
+
const targets = entity?.associations?.targets?.items ?? [];
|
|
3154
|
+
return targets
|
|
3155
|
+
.filter((t) => t !== null)
|
|
3156
|
+
.map(t => ({
|
|
3157
|
+
rtId: t.rtId,
|
|
3158
|
+
ckTypeId: t.ckTypeId,
|
|
3159
|
+
rtWellKnownName: t.rtWellKnownName ?? undefined,
|
|
3160
|
+
attributes: (t.attributes?.items ?? [])
|
|
3161
|
+
.filter((a) => a !== null && a.attributeName !== null)
|
|
3162
|
+
.map(a => ({
|
|
3163
|
+
attributeName: a.attributeName,
|
|
3164
|
+
value: a.value
|
|
3165
|
+
}))
|
|
3166
|
+
}));
|
|
3167
|
+
}));
|
|
3168
|
+
}
|
|
3169
|
+
/**
|
|
3170
|
+
* Fetches CK type attribute definitions (name + type) for config dialog.
|
|
3171
|
+
*/
|
|
3172
|
+
fetchCkTypeAttributes(ckTypeId) {
|
|
3173
|
+
return this.getCkTypeAttributesGQL.fetch({
|
|
3174
|
+
variables: {
|
|
3175
|
+
ckTypeId,
|
|
3176
|
+
first: 200
|
|
3177
|
+
}
|
|
3178
|
+
}).pipe(map(result => {
|
|
3179
|
+
const ckType = result.data?.constructionKit?.types?.items?.[0];
|
|
3180
|
+
const attrs = ckType?.attributes?.items ?? [];
|
|
3181
|
+
return attrs
|
|
3182
|
+
.filter((a) => a !== null)
|
|
3183
|
+
.map(a => ({
|
|
3184
|
+
attributeName: a.attributeName,
|
|
3185
|
+
attributeValueType: a.attributeValueType
|
|
3186
|
+
}));
|
|
3187
|
+
}));
|
|
3188
|
+
}
|
|
3189
|
+
// ============================================================================
|
|
3053
3190
|
// Private Helper Methods
|
|
3054
3191
|
// ============================================================================
|
|
3055
3192
|
/**
|
|
@@ -5506,6 +5643,9 @@ class EntityAssociationsWidgetComponent {
|
|
|
5506
5643
|
_data = signal(null, ...(ngDevMode ? [{ debugName: "_data" }] : []));
|
|
5507
5644
|
_error = signal(null, ...(ngDevMode ? [{ debugName: "_error" }] : []));
|
|
5508
5645
|
_expandedGroups = signal(new Set(), ...(ngDevMode ? [{ debugName: "_expandedGroups" }] : []));
|
|
5646
|
+
// Cache for target entity attributes per group (key = roleId_direction)
|
|
5647
|
+
_targetAttributesCache = signal(new Map(), ...(ngDevMode ? [{ debugName: "_targetAttributesCache" }] : []));
|
|
5648
|
+
_loadingTargetGroups = signal(new Set(), ...(ngDevMode ? [{ debugName: "_loadingTargetGroups" }] : []));
|
|
5509
5649
|
// Detail dialog state
|
|
5510
5650
|
showDetailDialog = false;
|
|
5511
5651
|
detailEntityRtId = '';
|
|
@@ -5545,6 +5685,16 @@ class EntityAssociationsWidgetComponent {
|
|
|
5545
5685
|
displayMode = computed(() => {
|
|
5546
5686
|
return this.config?.displayMode ?? 'expandable';
|
|
5547
5687
|
}, ...(ngDevMode ? [{ debugName: "displayMode" }] : []));
|
|
5688
|
+
/**
|
|
5689
|
+
* Filtered source entity attributes based on configured entityAttributePaths
|
|
5690
|
+
*/
|
|
5691
|
+
filteredEntityAttributes = computed(() => {
|
|
5692
|
+
const data = this._data();
|
|
5693
|
+
const paths = this.config?.entityAttributePaths;
|
|
5694
|
+
if (!data?.attributes || !paths?.length)
|
|
5695
|
+
return [];
|
|
5696
|
+
return data.attributes.filter(a => paths.includes(a.attributeName));
|
|
5697
|
+
}, ...(ngDevMode ? [{ debugName: "filteredEntityAttributes" }] : []));
|
|
5548
5698
|
groupedAssociations = computed(() => {
|
|
5549
5699
|
const data = this._data();
|
|
5550
5700
|
if (!data?.associations)
|
|
@@ -5622,6 +5772,8 @@ class EntityAssociationsWidgetComponent {
|
|
|
5622
5772
|
}
|
|
5623
5773
|
else {
|
|
5624
5774
|
expanded.add(key);
|
|
5775
|
+
// Load target attributes when expanding, if configured
|
|
5776
|
+
this.loadTargetAttributes(group);
|
|
5625
5777
|
}
|
|
5626
5778
|
this._expandedGroups.set(expanded);
|
|
5627
5779
|
// Update the group's expanded state
|
|
@@ -5632,6 +5784,18 @@ class EntityAssociationsWidgetComponent {
|
|
|
5632
5784
|
return this._expandedGroups().has(key);
|
|
5633
5785
|
}
|
|
5634
5786
|
getTargetEntities(group) {
|
|
5787
|
+
const cacheKey = this.getGroupCacheKey(group);
|
|
5788
|
+
const cached = this._targetAttributesCache().get(cacheKey);
|
|
5789
|
+
if (cached) {
|
|
5790
|
+
// Return enriched targets from cache
|
|
5791
|
+
return cached.map(t => ({
|
|
5792
|
+
rtId: t.rtId,
|
|
5793
|
+
ckTypeId: t.ckTypeId,
|
|
5794
|
+
displayName: t.rtWellKnownName || t.rtId,
|
|
5795
|
+
attributes: t.attributes
|
|
5796
|
+
}));
|
|
5797
|
+
}
|
|
5798
|
+
// Fall back to basic association data
|
|
5635
5799
|
const sourceRtId = this._data()?.rtId;
|
|
5636
5800
|
return group.associations.map(assoc => {
|
|
5637
5801
|
const isOutgoing = assoc.originRtId === sourceRtId;
|
|
@@ -5640,10 +5804,81 @@ class EntityAssociationsWidgetComponent {
|
|
|
5640
5804
|
return {
|
|
5641
5805
|
rtId,
|
|
5642
5806
|
ckTypeId,
|
|
5643
|
-
displayName: rtId
|
|
5807
|
+
displayName: rtId
|
|
5644
5808
|
};
|
|
5645
5809
|
});
|
|
5646
5810
|
}
|
|
5811
|
+
isLoadingTargets(group) {
|
|
5812
|
+
return this._loadingTargetGroups().has(this.getGroupCacheKey(group));
|
|
5813
|
+
}
|
|
5814
|
+
formatAttributeName(name) {
|
|
5815
|
+
// camelCase/PascalCase → "Title Case"
|
|
5816
|
+
return name
|
|
5817
|
+
.replace(/([A-Z])/g, ' $1')
|
|
5818
|
+
.replace(/^./, str => str.toUpperCase())
|
|
5819
|
+
.trim();
|
|
5820
|
+
}
|
|
5821
|
+
formatValue(value) {
|
|
5822
|
+
if (value === null || value === undefined)
|
|
5823
|
+
return '–';
|
|
5824
|
+
if (typeof value === 'boolean')
|
|
5825
|
+
return value ? 'Yes' : 'No';
|
|
5826
|
+
if (typeof value === 'number') {
|
|
5827
|
+
return value.toLocaleString('de-AT', {
|
|
5828
|
+
minimumFractionDigits: value % 1 !== 0 ? 1 : 0,
|
|
5829
|
+
maximumFractionDigits: 2
|
|
5830
|
+
});
|
|
5831
|
+
}
|
|
5832
|
+
if (value instanceof Date)
|
|
5833
|
+
return value.toLocaleDateString('de-AT');
|
|
5834
|
+
if (typeof value === 'string') {
|
|
5835
|
+
// Try to detect ISO date strings
|
|
5836
|
+
if (/^\d{4}-\d{2}-\d{2}T/.test(value)) {
|
|
5837
|
+
const date = new Date(value);
|
|
5838
|
+
if (!isNaN(date.getTime()))
|
|
5839
|
+
return date.toLocaleDateString('de-AT');
|
|
5840
|
+
}
|
|
5841
|
+
return value;
|
|
5842
|
+
}
|
|
5843
|
+
return String(value);
|
|
5844
|
+
}
|
|
5845
|
+
getGroupCacheKey(group) {
|
|
5846
|
+
return `${group.direction}-${group.roleId}-${group.targetType}`;
|
|
5847
|
+
}
|
|
5848
|
+
loadTargetAttributes(group) {
|
|
5849
|
+
const targetAttributePaths = this.config?.targetAttributePaths;
|
|
5850
|
+
if (!targetAttributePaths?.length)
|
|
5851
|
+
return;
|
|
5852
|
+
const cacheKey = this.getGroupCacheKey(group);
|
|
5853
|
+
// Skip if already cached or loading
|
|
5854
|
+
if (this._targetAttributesCache().has(cacheKey) || this._loadingTargetGroups().has(cacheKey))
|
|
5855
|
+
return;
|
|
5856
|
+
const data = this._data();
|
|
5857
|
+
if (!data)
|
|
5858
|
+
return;
|
|
5859
|
+
// Mark as loading
|
|
5860
|
+
const loading = new Set(this._loadingTargetGroups());
|
|
5861
|
+
loading.add(cacheKey);
|
|
5862
|
+
this._loadingTargetGroups.set(loading);
|
|
5863
|
+
// Determine target CK type from the group's associations
|
|
5864
|
+
const sourceRtId = data.rtId;
|
|
5865
|
+
const firstAssoc = group.associations[0];
|
|
5866
|
+
const isOutgoing = firstAssoc.originRtId === sourceRtId;
|
|
5867
|
+
const targetCkTypeId = isOutgoing ? firstAssoc.targetCkTypeId : firstAssoc.originCkTypeId;
|
|
5868
|
+
this.dataService.fetchAssociationTargets(data.rtId, data.ckTypeId, targetCkTypeId, group.roleId, group.direction, targetAttributePaths, group.count).pipe(catchError(err => {
|
|
5869
|
+
console.error('Error loading target attributes:', err);
|
|
5870
|
+
return of([]);
|
|
5871
|
+
})).subscribe(targets => {
|
|
5872
|
+
// Update cache
|
|
5873
|
+
const cache = new Map(this._targetAttributesCache());
|
|
5874
|
+
cache.set(cacheKey, targets);
|
|
5875
|
+
this._targetAttributesCache.set(cache);
|
|
5876
|
+
// Remove from loading
|
|
5877
|
+
const loadingState = new Set(this._loadingTargetGroups());
|
|
5878
|
+
loadingState.delete(cacheKey);
|
|
5879
|
+
this._loadingTargetGroups.set(loadingState);
|
|
5880
|
+
});
|
|
5881
|
+
}
|
|
5647
5882
|
onTargetClick(target) {
|
|
5648
5883
|
this.detailEntityRtId = target.rtId;
|
|
5649
5884
|
this.detailEntityCkTypeId = target.ckTypeId;
|
|
@@ -5670,6 +5905,8 @@ class EntityAssociationsWidgetComponent {
|
|
|
5670
5905
|
if (dataSource.type === 'runtimeEntity') {
|
|
5671
5906
|
this._isLoading.set(true);
|
|
5672
5907
|
this._error.set(null);
|
|
5908
|
+
this._targetAttributesCache.set(new Map());
|
|
5909
|
+
this._loadingTargetGroups.set(new Set());
|
|
5673
5910
|
this.dataService.fetchEntityWithAssociations(dataSource.rtId, dataSource.ckTypeId)
|
|
5674
5911
|
.pipe(catchError(err => {
|
|
5675
5912
|
console.error('Error loading associations data:', err);
|
|
@@ -5679,6 +5916,14 @@ class EntityAssociationsWidgetComponent {
|
|
|
5679
5916
|
.subscribe(entityData => {
|
|
5680
5917
|
this._data.set(entityData);
|
|
5681
5918
|
this._isLoading.set(false);
|
|
5919
|
+
// Re-load target attributes for any currently expanded groups
|
|
5920
|
+
if (entityData && this.config?.targetAttributePaths?.length) {
|
|
5921
|
+
for (const group of this.groupedAssociations()) {
|
|
5922
|
+
if (this.isGroupExpanded(group)) {
|
|
5923
|
+
this.loadTargetAttributes(group);
|
|
5924
|
+
}
|
|
5925
|
+
}
|
|
5926
|
+
}
|
|
5682
5927
|
});
|
|
5683
5928
|
}
|
|
5684
5929
|
}
|
|
@@ -5693,11 +5938,11 @@ class EntityAssociationsWidgetComponent {
|
|
|
5693
5938
|
.trim();
|
|
5694
5939
|
}
|
|
5695
5940
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: EntityAssociationsWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5696
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: EntityAssociationsWidgetComponent, isStandalone: true, selector: "mm-entity-associations-widget", inputs: { config: "config" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"associations-widget\" [class.no-data]=\"!data()\" [class.loading]=\"isLoading()\" [class.error]=\"error()\">\n @if (isNotConfigured()) {\n <mm-widget-not-configured></mm-widget-not-configured>\n } @else if (isLoading()) {\n <div class=\"loading-indicator\">\n <span>Loading...</span>\n </div>\n } @else if (error()) {\n <div class=\"error-message\">\n <span>{{ error() }}</span>\n </div>\n } @else if (data()) {\n <!-- Entity Info Header -->\n <div class=\"entity-header\">\n <div class=\"entity-box\">\n <span class=\"entity-name\">{{ entityName() }}</span>\n </div>\n <div class=\"entity-details\">\n <span class=\"entity-id\" title=\"{{ entityCkTypeId() }}\">{{ entityRtId() }}</span>\n </div>\n </div>\n\n <!-- Associations List -->\n <div class=\"associations-container\">\n @for (group of groupedAssociations(); track group.roleId + group.direction) {\n <div class=\"association-group\" [class.expanded]=\"isGroupExpanded(group)\">\n <!-- Group Header -->\n <div class=\"group-header\"\n [class.clickable]=\"displayMode() === 'expandable'\"\n (click)=\"toggleGroup(group)\">\n <div class=\"direction-indicator\">\n @if (group.direction === 'out') {\n <kendo-svg-icon [icon]=\"arrowRightIcon\" size=\"small\"></kendo-svg-icon>\n } @else {\n <kendo-svg-icon [icon]=\"arrowLeftIcon\" size=\"small\"></kendo-svg-icon>\n }\n </div>\n <div class=\"group-info\">\n <span class=\"role-name\">{{ group.roleName }}</span>\n <span class=\"target-type\">{{ group.targetType }}</span>\n <span class=\"count-badge\">{{ group.count }}</span>\n </div>\n @if (displayMode() === 'expandable') {\n <div class=\"expand-icon\">\n @if (isGroupExpanded(group)) {\n <kendo-svg-icon [icon]=\"chevronDownIcon\" size=\"small\"></kendo-svg-icon>\n } @else {\n <kendo-svg-icon [icon]=\"chevronRightIcon\" size=\"small\"></kendo-svg-icon>\n }\n </div>\n }\n </div>\n\n <!-- Expanded Target List -->\n @if (displayMode() === 'expandable' && isGroupExpanded(group)) {\n <div class=\"target-list\">\n @for (target of getTargetEntities(group); track target.rtId) {\n <div class=\"target-item\" (click)=\"onTargetClick(target)\">\n
|
|
5941
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: EntityAssociationsWidgetComponent, isStandalone: true, selector: "mm-entity-associations-widget", inputs: { config: "config" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"associations-widget\" [class.no-data]=\"!data()\" [class.loading]=\"isLoading()\" [class.error]=\"error()\">\n @if (isNotConfigured()) {\n <mm-widget-not-configured></mm-widget-not-configured>\n } @else if (isLoading()) {\n <div class=\"loading-indicator\">\n <span>Loading...</span>\n </div>\n } @else if (error()) {\n <div class=\"error-message\">\n <span>{{ error() }}</span>\n </div>\n } @else if (data()) {\n <!-- Entity Info Header -->\n <div class=\"entity-header\">\n <div class=\"entity-box\">\n <span class=\"entity-name\">{{ entityName() }}</span>\n </div>\n <div class=\"entity-details\">\n <span class=\"entity-id\" title=\"{{ entityCkTypeId() }}\">{{ entityRtId() }}</span>\n </div>\n </div>\n\n <!-- Entity Attributes (Source) -->\n @if (filteredEntityAttributes().length > 0) {\n <div class=\"entity-attributes\">\n @for (attr of filteredEntityAttributes(); track attr.attributeName) {\n <div class=\"attribute-row\">\n <span class=\"attribute-name\">{{ formatAttributeName(attr.attributeName) }}</span>\n <span class=\"attribute-value\">{{ formatValue(attr.value) }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Associations List -->\n <div class=\"associations-container\">\n @for (group of groupedAssociations(); track group.roleId + group.direction) {\n <div class=\"association-group\" [class.expanded]=\"isGroupExpanded(group)\">\n <!-- Group Header -->\n <div class=\"group-header\"\n [class.clickable]=\"displayMode() === 'expandable'\"\n (click)=\"toggleGroup(group)\">\n <div class=\"direction-indicator\">\n @if (group.direction === 'out') {\n <kendo-svg-icon [icon]=\"arrowRightIcon\" size=\"small\"></kendo-svg-icon>\n } @else {\n <kendo-svg-icon [icon]=\"arrowLeftIcon\" size=\"small\"></kendo-svg-icon>\n }\n </div>\n <div class=\"group-info\">\n <span class=\"role-name\">{{ group.roleName }}</span>\n <span class=\"target-type\">{{ group.targetType }}</span>\n <span class=\"count-badge\">{{ group.count }}</span>\n </div>\n @if (displayMode() === 'expandable') {\n <div class=\"expand-icon\">\n @if (isGroupExpanded(group)) {\n <kendo-svg-icon [icon]=\"chevronDownIcon\" size=\"small\"></kendo-svg-icon>\n } @else {\n <kendo-svg-icon [icon]=\"chevronRightIcon\" size=\"small\"></kendo-svg-icon>\n }\n </div>\n }\n </div>\n\n <!-- Expanded Target List -->\n @if (displayMode() === 'expandable' && isGroupExpanded(group)) {\n <div class=\"target-list\">\n @if (isLoadingTargets(group)) {\n <div class=\"target-loading\">Loading attributes...</div>\n }\n @for (target of getTargetEntities(group); track target.rtId) {\n <div class=\"target-entry\">\n <div class=\"target-item\" (click)=\"onTargetClick(target)\">\n <span class=\"target-id\">{{ target.displayName }}</span>\n <span class=\"target-type-hint\" [title]=\"target.ckTypeId\">{{ target.ckTypeId }}</span>\n </div>\n @if (target.attributes?.length) {\n <div class=\"target-attributes\">\n @for (attr of target.attributes; track attr.attributeName) {\n <div class=\"target-attr-row\">\n <span class=\"target-attr-name\">{{ formatAttributeName(attr.attributeName) }}</span>\n <span class=\"target-attr-value\">{{ formatValue(attr.value) }}</span>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Summary Footer -->\n <div class=\"associations-summary\">\n <kendo-svg-icon [icon]=\"linkIcon\" size=\"small\"></kendo-svg-icon>\n <span>{{ totalAssociations() }} relationship(s)</span>\n </div>\n } @else {\n <mm-widget-not-configured></mm-widget-not-configured>\n }\n</div>\n\n<!-- Entity Detail Dialog -->\n@if (showDetailDialog) {\n <mm-entity-detail-dialog\n [rtId]=\"detailEntityRtId\"\n [ckTypeId]=\"detailEntityCkTypeId\"\n (closed)=\"onDetailDialogClosed()\">\n </mm-entity-detail-dialog>\n}\n", styles: [".associations-widget{display:flex;flex-direction:column;height:100%}.associations-widget.no-data{justify-content:center;align-items:center;color:var(--kendo-color-subtle, #6c757d)}.entity-header{display:flex;align-items:center;gap:12px;padding:8px 12px;background:var(--kendo-color-surface-alt, #f8f9fa);border-bottom:1px solid var(--kendo-color-border, #dee2e6)}.entity-header .entity-box{display:flex;align-items:center;justify-content:center;padding:8px 16px;background:var(--kendo-color-primary, #0d6efd);color:var(--kendo-color-on-primary, #fff);border-radius:4px;font-weight:600;font-size:.875rem}.entity-header .entity-details{display:flex;flex-direction:column;gap:2px}.entity-header .entity-id{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);font-family:monospace}.entity-attributes{padding:4px 12px 8px;border-bottom:1px solid var(--kendo-color-border, #dee2e6)}.entity-attributes .attribute-row{display:flex;justify-content:space-between;padding:2px 0;font-size:.8125rem}.entity-attributes .attribute-name{color:var(--kendo-color-subtle, #6c757d)}.entity-attributes .attribute-value{font-weight:500;color:var(--kendo-color-on-surface, #212529)}.associations-container{flex:1;overflow:auto;padding:8px;display:flex;flex-direction:column;gap:4px}.association-group{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;overflow:hidden}.association-group.expanded .group-header{border-bottom:1px solid var(--kendo-color-border, #dee2e6)}.group-header{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--kendo-color-surface-alt, #f8f9fa)}.group-header.clickable{cursor:pointer}.group-header.clickable:hover{background:var(--kendo-color-surface, #ffffff)}.direction-indicator{color:var(--kendo-color-subtle, #6c757d);display:flex;align-items:center;width:20px}.group-info{display:flex;align-items:center;gap:8px;flex:1;font-size:.8125rem}.group-info .role-name{color:var(--kendo-color-subtle, #6c757d);font-style:italic}.group-info .target-type{font-weight:500;color:var(--kendo-color-on-surface, #212529)}.group-info .count-badge{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;background:var(--kendo-color-primary, #0d6efd);color:var(--kendo-color-on-primary, #fff);border-radius:10px;font-size:.6875rem;font-weight:600}.expand-icon{color:var(--kendo-color-subtle, #6c757d);display:flex;align-items:center}.target-list{padding:4px 0;max-height:200px;overflow-y:auto}.target-entry+.target-entry{border-top:1px solid var(--kendo-color-border, #dee2e6)}.target-item{display:flex;align-items:center;gap:8px;padding:6px 12px 6px 40px;cursor:pointer;font-size:.8125rem;transition:background .15s ease}.target-item:hover{background:var(--kendo-color-surface-alt, #f8f9fa)}.target-item .target-id{font-family:monospace;color:var(--kendo-color-on-surface, #212529);flex-shrink:0}.target-item .target-type-hint{font-size:.6875rem;color:var(--kendo-color-subtle, #6c757d);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;flex-shrink:1}.target-attributes{padding:2px 12px 6px 56px}.target-attributes .target-attr-row{display:flex;justify-content:space-between;padding:1px 0;font-size:.75rem}.target-attributes .target-attr-name{color:var(--kendo-color-subtle, #6c757d)}.target-attributes .target-attr-value{color:var(--kendo-color-on-surface, #212529);font-weight:500}.target-loading{padding:6px 12px 6px 40px;font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);font-style:italic}.associations-summary{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--kendo-color-surface-alt, #f8f9fa);border-top:1px solid var(--kendo-color-border, #dee2e6);font-size:.75rem;color:var(--kendo-color-subtle, #6c757d)}.no-data-message{font-size:.875rem}.loading-indicator{display:flex;align-items:center;justify-content:center;height:100%;color:var(--kendo-color-subtle, #6c757d)}.error-message{display:flex;align-items:center;justify-content:center;height:100%;color:var(--kendo-color-error, #dc3545)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i2$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: EntityDetailDialogComponent, selector: "mm-entity-detail-dialog", inputs: ["rtId", "ckTypeId"], outputs: ["closed"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }] });
|
|
5697
5942
|
}
|
|
5698
5943
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: EntityAssociationsWidgetComponent, decorators: [{
|
|
5699
5944
|
type: Component,
|
|
5700
|
-
args: [{ selector: 'mm-entity-associations-widget', standalone: true, imports: [CommonModule, SVGIconModule, EntityDetailDialogComponent, WidgetNotConfiguredComponent], template: "<div class=\"associations-widget\" [class.no-data]=\"!data()\" [class.loading]=\"isLoading()\" [class.error]=\"error()\">\n @if (isNotConfigured()) {\n <mm-widget-not-configured></mm-widget-not-configured>\n } @else if (isLoading()) {\n <div class=\"loading-indicator\">\n <span>Loading...</span>\n </div>\n } @else if (error()) {\n <div class=\"error-message\">\n <span>{{ error() }}</span>\n </div>\n } @else if (data()) {\n <!-- Entity Info Header -->\n <div class=\"entity-header\">\n <div class=\"entity-box\">\n <span class=\"entity-name\">{{ entityName() }}</span>\n </div>\n <div class=\"entity-details\">\n <span class=\"entity-id\" title=\"{{ entityCkTypeId() }}\">{{ entityRtId() }}</span>\n </div>\n </div>\n\n <!-- Associations List -->\n <div class=\"associations-container\">\n @for (group of groupedAssociations(); track group.roleId + group.direction) {\n <div class=\"association-group\" [class.expanded]=\"isGroupExpanded(group)\">\n <!-- Group Header -->\n <div class=\"group-header\"\n [class.clickable]=\"displayMode() === 'expandable'\"\n (click)=\"toggleGroup(group)\">\n <div class=\"direction-indicator\">\n @if (group.direction === 'out') {\n <kendo-svg-icon [icon]=\"arrowRightIcon\" size=\"small\"></kendo-svg-icon>\n } @else {\n <kendo-svg-icon [icon]=\"arrowLeftIcon\" size=\"small\"></kendo-svg-icon>\n }\n </div>\n <div class=\"group-info\">\n <span class=\"role-name\">{{ group.roleName }}</span>\n <span class=\"target-type\">{{ group.targetType }}</span>\n <span class=\"count-badge\">{{ group.count }}</span>\n </div>\n @if (displayMode() === 'expandable') {\n <div class=\"expand-icon\">\n @if (isGroupExpanded(group)) {\n <kendo-svg-icon [icon]=\"chevronDownIcon\" size=\"small\"></kendo-svg-icon>\n } @else {\n <kendo-svg-icon [icon]=\"chevronRightIcon\" size=\"small\"></kendo-svg-icon>\n }\n </div>\n }\n </div>\n\n <!-- Expanded Target List -->\n @if (displayMode() === 'expandable' && isGroupExpanded(group)) {\n <div class=\"target-list\">\n @for (target of getTargetEntities(group); track target.rtId) {\n <div class=\"target-item\" (click)=\"onTargetClick(target)\">\n
|
|
5945
|
+
args: [{ selector: 'mm-entity-associations-widget', standalone: true, imports: [CommonModule, SVGIconModule, EntityDetailDialogComponent, WidgetNotConfiguredComponent], template: "<div class=\"associations-widget\" [class.no-data]=\"!data()\" [class.loading]=\"isLoading()\" [class.error]=\"error()\">\n @if (isNotConfigured()) {\n <mm-widget-not-configured></mm-widget-not-configured>\n } @else if (isLoading()) {\n <div class=\"loading-indicator\">\n <span>Loading...</span>\n </div>\n } @else if (error()) {\n <div class=\"error-message\">\n <span>{{ error() }}</span>\n </div>\n } @else if (data()) {\n <!-- Entity Info Header -->\n <div class=\"entity-header\">\n <div class=\"entity-box\">\n <span class=\"entity-name\">{{ entityName() }}</span>\n </div>\n <div class=\"entity-details\">\n <span class=\"entity-id\" title=\"{{ entityCkTypeId() }}\">{{ entityRtId() }}</span>\n </div>\n </div>\n\n <!-- Entity Attributes (Source) -->\n @if (filteredEntityAttributes().length > 0) {\n <div class=\"entity-attributes\">\n @for (attr of filteredEntityAttributes(); track attr.attributeName) {\n <div class=\"attribute-row\">\n <span class=\"attribute-name\">{{ formatAttributeName(attr.attributeName) }}</span>\n <span class=\"attribute-value\">{{ formatValue(attr.value) }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Associations List -->\n <div class=\"associations-container\">\n @for (group of groupedAssociations(); track group.roleId + group.direction) {\n <div class=\"association-group\" [class.expanded]=\"isGroupExpanded(group)\">\n <!-- Group Header -->\n <div class=\"group-header\"\n [class.clickable]=\"displayMode() === 'expandable'\"\n (click)=\"toggleGroup(group)\">\n <div class=\"direction-indicator\">\n @if (group.direction === 'out') {\n <kendo-svg-icon [icon]=\"arrowRightIcon\" size=\"small\"></kendo-svg-icon>\n } @else {\n <kendo-svg-icon [icon]=\"arrowLeftIcon\" size=\"small\"></kendo-svg-icon>\n }\n </div>\n <div class=\"group-info\">\n <span class=\"role-name\">{{ group.roleName }}</span>\n <span class=\"target-type\">{{ group.targetType }}</span>\n <span class=\"count-badge\">{{ group.count }}</span>\n </div>\n @if (displayMode() === 'expandable') {\n <div class=\"expand-icon\">\n @if (isGroupExpanded(group)) {\n <kendo-svg-icon [icon]=\"chevronDownIcon\" size=\"small\"></kendo-svg-icon>\n } @else {\n <kendo-svg-icon [icon]=\"chevronRightIcon\" size=\"small\"></kendo-svg-icon>\n }\n </div>\n }\n </div>\n\n <!-- Expanded Target List -->\n @if (displayMode() === 'expandable' && isGroupExpanded(group)) {\n <div class=\"target-list\">\n @if (isLoadingTargets(group)) {\n <div class=\"target-loading\">Loading attributes...</div>\n }\n @for (target of getTargetEntities(group); track target.rtId) {\n <div class=\"target-entry\">\n <div class=\"target-item\" (click)=\"onTargetClick(target)\">\n <span class=\"target-id\">{{ target.displayName }}</span>\n <span class=\"target-type-hint\" [title]=\"target.ckTypeId\">{{ target.ckTypeId }}</span>\n </div>\n @if (target.attributes?.length) {\n <div class=\"target-attributes\">\n @for (attr of target.attributes; track attr.attributeName) {\n <div class=\"target-attr-row\">\n <span class=\"target-attr-name\">{{ formatAttributeName(attr.attributeName) }}</span>\n <span class=\"target-attr-value\">{{ formatValue(attr.value) }}</span>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Summary Footer -->\n <div class=\"associations-summary\">\n <kendo-svg-icon [icon]=\"linkIcon\" size=\"small\"></kendo-svg-icon>\n <span>{{ totalAssociations() }} relationship(s)</span>\n </div>\n } @else {\n <mm-widget-not-configured></mm-widget-not-configured>\n }\n</div>\n\n<!-- Entity Detail Dialog -->\n@if (showDetailDialog) {\n <mm-entity-detail-dialog\n [rtId]=\"detailEntityRtId\"\n [ckTypeId]=\"detailEntityCkTypeId\"\n (closed)=\"onDetailDialogClosed()\">\n </mm-entity-detail-dialog>\n}\n", styles: [".associations-widget{display:flex;flex-direction:column;height:100%}.associations-widget.no-data{justify-content:center;align-items:center;color:var(--kendo-color-subtle, #6c757d)}.entity-header{display:flex;align-items:center;gap:12px;padding:8px 12px;background:var(--kendo-color-surface-alt, #f8f9fa);border-bottom:1px solid var(--kendo-color-border, #dee2e6)}.entity-header .entity-box{display:flex;align-items:center;justify-content:center;padding:8px 16px;background:var(--kendo-color-primary, #0d6efd);color:var(--kendo-color-on-primary, #fff);border-radius:4px;font-weight:600;font-size:.875rem}.entity-header .entity-details{display:flex;flex-direction:column;gap:2px}.entity-header .entity-id{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);font-family:monospace}.entity-attributes{padding:4px 12px 8px;border-bottom:1px solid var(--kendo-color-border, #dee2e6)}.entity-attributes .attribute-row{display:flex;justify-content:space-between;padding:2px 0;font-size:.8125rem}.entity-attributes .attribute-name{color:var(--kendo-color-subtle, #6c757d)}.entity-attributes .attribute-value{font-weight:500;color:var(--kendo-color-on-surface, #212529)}.associations-container{flex:1;overflow:auto;padding:8px;display:flex;flex-direction:column;gap:4px}.association-group{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;overflow:hidden}.association-group.expanded .group-header{border-bottom:1px solid var(--kendo-color-border, #dee2e6)}.group-header{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--kendo-color-surface-alt, #f8f9fa)}.group-header.clickable{cursor:pointer}.group-header.clickable:hover{background:var(--kendo-color-surface, #ffffff)}.direction-indicator{color:var(--kendo-color-subtle, #6c757d);display:flex;align-items:center;width:20px}.group-info{display:flex;align-items:center;gap:8px;flex:1;font-size:.8125rem}.group-info .role-name{color:var(--kendo-color-subtle, #6c757d);font-style:italic}.group-info .target-type{font-weight:500;color:var(--kendo-color-on-surface, #212529)}.group-info .count-badge{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;background:var(--kendo-color-primary, #0d6efd);color:var(--kendo-color-on-primary, #fff);border-radius:10px;font-size:.6875rem;font-weight:600}.expand-icon{color:var(--kendo-color-subtle, #6c757d);display:flex;align-items:center}.target-list{padding:4px 0;max-height:200px;overflow-y:auto}.target-entry+.target-entry{border-top:1px solid var(--kendo-color-border, #dee2e6)}.target-item{display:flex;align-items:center;gap:8px;padding:6px 12px 6px 40px;cursor:pointer;font-size:.8125rem;transition:background .15s ease}.target-item:hover{background:var(--kendo-color-surface-alt, #f8f9fa)}.target-item .target-id{font-family:monospace;color:var(--kendo-color-on-surface, #212529);flex-shrink:0}.target-item .target-type-hint{font-size:.6875rem;color:var(--kendo-color-subtle, #6c757d);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;flex-shrink:1}.target-attributes{padding:2px 12px 6px 56px}.target-attributes .target-attr-row{display:flex;justify-content:space-between;padding:1px 0;font-size:.75rem}.target-attributes .target-attr-name{color:var(--kendo-color-subtle, #6c757d)}.target-attributes .target-attr-value{color:var(--kendo-color-on-surface, #212529);font-weight:500}.target-loading{padding:6px 12px 6px 40px;font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);font-style:italic}.associations-summary{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--kendo-color-surface-alt, #f8f9fa);border-top:1px solid var(--kendo-color-border, #dee2e6);font-size:.75rem;color:var(--kendo-color-subtle, #6c757d)}.no-data-message{font-size:.875rem}.loading-indicator{display:flex;align-items:center;justify-content:center;height:100%;color:var(--kendo-color-subtle, #6c757d)}.error-message{display:flex;align-items:center;justify-content:center;height:100%;color:var(--kendo-color-error, #dc3545)}\n"] }]
|
|
5701
5946
|
}], propDecorators: { config: [{
|
|
5702
5947
|
type: Input
|
|
5703
5948
|
}] } });
|
|
@@ -5715,11 +5960,13 @@ const GetCkTypeAssociationRolesDocumentDto = gql `
|
|
|
5715
5960
|
fullName
|
|
5716
5961
|
semanticVersionedFullName
|
|
5717
5962
|
}
|
|
5963
|
+
rtRoleId
|
|
5718
5964
|
navigationPropertyName
|
|
5719
5965
|
multiplicity
|
|
5720
5966
|
targetCkTypeId {
|
|
5721
5967
|
fullName
|
|
5722
5968
|
}
|
|
5969
|
+
rtTargetCkTypeId
|
|
5723
5970
|
}
|
|
5724
5971
|
}
|
|
5725
5972
|
out {
|
|
@@ -5728,11 +5975,13 @@ const GetCkTypeAssociationRolesDocumentDto = gql `
|
|
|
5728
5975
|
fullName
|
|
5729
5976
|
semanticVersionedFullName
|
|
5730
5977
|
}
|
|
5978
|
+
rtRoleId
|
|
5731
5979
|
navigationPropertyName
|
|
5732
5980
|
multiplicity
|
|
5733
5981
|
targetCkTypeId {
|
|
5734
5982
|
fullName
|
|
5735
5983
|
}
|
|
5984
|
+
rtTargetCkTypeId
|
|
5736
5985
|
}
|
|
5737
5986
|
}
|
|
5738
5987
|
}
|
|
@@ -5860,6 +6109,7 @@ class AssociationsConfigDialogComponent {
|
|
|
5860
6109
|
getEntitiesByCkTypeGQL = inject(GetEntitiesByCkTypeDtoGQL);
|
|
5861
6110
|
getCkTypeAssociationRolesGQL = inject(GetCkTypeAssociationRolesDtoGQL);
|
|
5862
6111
|
ckTypeSelectorService = inject(CkTypeSelectorService);
|
|
6112
|
+
dataService = inject(MeshBoardDataService);
|
|
5863
6113
|
windowRef = inject(WindowRef);
|
|
5864
6114
|
initialCkTypeId;
|
|
5865
6115
|
initialRtId;
|
|
@@ -5867,6 +6117,8 @@ class AssociationsConfigDialogComponent {
|
|
|
5867
6117
|
initialShowOutgoing;
|
|
5868
6118
|
initialRoleFilter;
|
|
5869
6119
|
initialDisplayMode;
|
|
6120
|
+
initialEntityAttributePaths;
|
|
6121
|
+
initialTargetAttributePaths;
|
|
5870
6122
|
selectedCkType = null;
|
|
5871
6123
|
selectedEntity = null;
|
|
5872
6124
|
entityDataSource;
|
|
@@ -5875,11 +6127,21 @@ class AssociationsConfigDialogComponent {
|
|
|
5875
6127
|
showOutgoing = true;
|
|
5876
6128
|
selectedRoles = [];
|
|
5877
6129
|
displayMode = 'expandable';
|
|
6130
|
+
selectedEntityAttributes = [];
|
|
6131
|
+
selectedTargetAttributes = [];
|
|
5878
6132
|
isLoadingInitial = false;
|
|
5879
6133
|
isLoadingRoles = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingRoles" }] : []));
|
|
5880
6134
|
availableRoles = signal([], ...(ngDevMode ? [{ debugName: "availableRoles" }] : []));
|
|
5881
6135
|
filteredRoles = signal([], ...(ngDevMode ? [{ debugName: "filteredRoles" }] : []));
|
|
6136
|
+
isLoadingEntityAttributes = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingEntityAttributes" }] : []));
|
|
6137
|
+
availableEntityAttributes = signal([], ...(ngDevMode ? [{ debugName: "availableEntityAttributes" }] : []));
|
|
6138
|
+
filteredEntityAttributes = signal([], ...(ngDevMode ? [{ debugName: "filteredEntityAttributes" }] : []));
|
|
6139
|
+
isLoadingTargetAttributes = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingTargetAttributes" }] : []));
|
|
6140
|
+
availableTargetAttributes = signal([], ...(ngDevMode ? [{ debugName: "availableTargetAttributes" }] : []));
|
|
6141
|
+
filteredTargetAttributes = signal([], ...(ngDevMode ? [{ debugName: "filteredTargetAttributes" }] : []));
|
|
5882
6142
|
roleFilterText = '';
|
|
6143
|
+
entityAttrFilterText = '';
|
|
6144
|
+
targetAttrFilterText = '';
|
|
5883
6145
|
get isValid() {
|
|
5884
6146
|
return this.selectedCkType !== null &&
|
|
5885
6147
|
this.selectedEntity !== null &&
|
|
@@ -5896,6 +6158,12 @@ class AssociationsConfigDialogComponent {
|
|
|
5896
6158
|
if (this.initialDisplayMode) {
|
|
5897
6159
|
this.displayMode = this.initialDisplayMode;
|
|
5898
6160
|
}
|
|
6161
|
+
if (this.initialEntityAttributePaths) {
|
|
6162
|
+
this.selectedEntityAttributes = [...this.initialEntityAttributePaths];
|
|
6163
|
+
}
|
|
6164
|
+
if (this.initialTargetAttributePaths) {
|
|
6165
|
+
this.selectedTargetAttributes = [...this.initialTargetAttributePaths];
|
|
6166
|
+
}
|
|
5899
6167
|
if (this.initialCkTypeId) {
|
|
5900
6168
|
await this.loadInitialValues();
|
|
5901
6169
|
}
|
|
@@ -5912,11 +6180,17 @@ class AssociationsConfigDialogComponent {
|
|
|
5912
6180
|
if (this.initialRtId && this.entityDataSource) {
|
|
5913
6181
|
await this.loadInitialEntity();
|
|
5914
6182
|
}
|
|
5915
|
-
//
|
|
6183
|
+
// Restore initial selections after onCkTypeSelected() (which resets them)
|
|
5916
6184
|
if (this.initialRoleFilter && this.initialRoleFilter.length > 0) {
|
|
5917
6185
|
const allRoles = this.availableRoles();
|
|
5918
6186
|
this.selectedRoles = allRoles.filter(r => this.initialRoleFilter.includes(r.roleId));
|
|
5919
6187
|
}
|
|
6188
|
+
if (this.initialEntityAttributePaths?.length) {
|
|
6189
|
+
this.selectedEntityAttributes = [...this.initialEntityAttributePaths];
|
|
6190
|
+
}
|
|
6191
|
+
if (this.initialTargetAttributePaths?.length) {
|
|
6192
|
+
this.selectedTargetAttributes = [...this.initialTargetAttributePaths];
|
|
6193
|
+
}
|
|
5920
6194
|
}
|
|
5921
6195
|
}
|
|
5922
6196
|
catch (error) {
|
|
@@ -5958,11 +6232,15 @@ class AssociationsConfigDialogComponent {
|
|
|
5958
6232
|
this.selectedCkType = ckType;
|
|
5959
6233
|
this.selectedEntity = null;
|
|
5960
6234
|
this.selectedRoles = [];
|
|
6235
|
+
this.selectedEntityAttributes = [];
|
|
5961
6236
|
// Create data sources for entity selection
|
|
5962
6237
|
this.entityDataSource = new RuntimeEntitySelectDataSource$1(this.getEntitiesByCkTypeGQL, ckType.rtCkTypeId);
|
|
5963
6238
|
this.entityDialogDataSource = new RuntimeEntityDialogDataSource$1(this.getEntitiesByCkTypeGQL, ckType.rtCkTypeId);
|
|
5964
|
-
// Load available association roles
|
|
5965
|
-
await
|
|
6239
|
+
// Load available association roles and entity attributes in parallel
|
|
6240
|
+
await Promise.all([
|
|
6241
|
+
this.loadAssociationRoles(ckType.rtCkTypeId),
|
|
6242
|
+
this.loadEntityAttributes(ckType.rtCkTypeId)
|
|
6243
|
+
]);
|
|
5966
6244
|
}
|
|
5967
6245
|
onCkTypeCleared() {
|
|
5968
6246
|
this.selectedCkType = null;
|
|
@@ -5972,6 +6250,12 @@ class AssociationsConfigDialogComponent {
|
|
|
5972
6250
|
this.availableRoles.set([]);
|
|
5973
6251
|
this.filteredRoles.set([]);
|
|
5974
6252
|
this.selectedRoles = [];
|
|
6253
|
+
this.availableEntityAttributes.set([]);
|
|
6254
|
+
this.filteredEntityAttributes.set([]);
|
|
6255
|
+
this.selectedEntityAttributes = [];
|
|
6256
|
+
this.availableTargetAttributes.set([]);
|
|
6257
|
+
this.filteredTargetAttributes.set([]);
|
|
6258
|
+
this.selectedTargetAttributes = [];
|
|
5975
6259
|
}
|
|
5976
6260
|
onEntitySelected(entity) {
|
|
5977
6261
|
this.selectedEntity = entity;
|
|
@@ -6011,12 +6295,12 @@ class AssociationsConfigDialogComponent {
|
|
|
6011
6295
|
if (!assoc)
|
|
6012
6296
|
continue;
|
|
6013
6297
|
roles.push({
|
|
6014
|
-
roleId: assoc.
|
|
6298
|
+
roleId: assoc.rtRoleId,
|
|
6015
6299
|
navigationPropertyName: assoc.navigationPropertyName,
|
|
6016
6300
|
direction: 'in',
|
|
6017
|
-
targetCkTypeId: assoc.
|
|
6301
|
+
targetCkTypeId: assoc.rtTargetCkTypeId,
|
|
6018
6302
|
multiplicity: assoc.multiplicity,
|
|
6019
|
-
displayName: `[IN] ${assoc.
|
|
6303
|
+
displayName: `[IN] ${assoc.rtRoleId} (${assoc.rtTargetCkTypeId})`
|
|
6020
6304
|
});
|
|
6021
6305
|
}
|
|
6022
6306
|
// Process outgoing associations
|
|
@@ -6025,16 +6309,18 @@ class AssociationsConfigDialogComponent {
|
|
|
6025
6309
|
if (!assoc)
|
|
6026
6310
|
continue;
|
|
6027
6311
|
roles.push({
|
|
6028
|
-
roleId: assoc.
|
|
6312
|
+
roleId: assoc.rtRoleId,
|
|
6029
6313
|
navigationPropertyName: assoc.navigationPropertyName,
|
|
6030
6314
|
direction: 'out',
|
|
6031
|
-
targetCkTypeId: assoc.
|
|
6315
|
+
targetCkTypeId: assoc.rtTargetCkTypeId,
|
|
6032
6316
|
multiplicity: assoc.multiplicity,
|
|
6033
|
-
displayName: `[OUT] ${assoc.
|
|
6317
|
+
displayName: `[OUT] ${assoc.rtRoleId} (${assoc.rtTargetCkTypeId})`
|
|
6034
6318
|
});
|
|
6035
6319
|
}
|
|
6036
6320
|
this.availableRoles.set(roles);
|
|
6037
6321
|
this.filteredRoles.set(roles);
|
|
6322
|
+
// Load target attributes from unique target CK types
|
|
6323
|
+
await this.loadTargetAttributesFromRoles(roles);
|
|
6038
6324
|
}
|
|
6039
6325
|
catch (error) {
|
|
6040
6326
|
console.error('Error loading association roles:', error);
|
|
@@ -6043,9 +6329,77 @@ class AssociationsConfigDialogComponent {
|
|
|
6043
6329
|
this.isLoadingRoles.set(false);
|
|
6044
6330
|
}
|
|
6045
6331
|
}
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6332
|
+
async loadEntityAttributes(ckTypeId) {
|
|
6333
|
+
this.isLoadingEntityAttributes.set(true);
|
|
6334
|
+
this.availableEntityAttributes.set([]);
|
|
6335
|
+
this.filteredEntityAttributes.set([]);
|
|
6336
|
+
try {
|
|
6337
|
+
const attrs = await firstValueFrom(this.dataService.fetchCkTypeAttributes(ckTypeId));
|
|
6338
|
+
this.availableEntityAttributes.set(attrs);
|
|
6339
|
+
this.filteredEntityAttributes.set(attrs);
|
|
6340
|
+
}
|
|
6341
|
+
catch (error) {
|
|
6342
|
+
console.error('Error loading entity attributes:', error);
|
|
6343
|
+
}
|
|
6344
|
+
finally {
|
|
6345
|
+
this.isLoadingEntityAttributes.set(false);
|
|
6346
|
+
}
|
|
6347
|
+
}
|
|
6348
|
+
async loadTargetAttributesFromRoles(roles) {
|
|
6349
|
+
// Collect unique target CK type IDs from the roles
|
|
6350
|
+
const targetCkTypeIds = [...new Set(roles.map(r => r.targetCkTypeId))];
|
|
6351
|
+
if (targetCkTypeIds.length === 0)
|
|
6352
|
+
return;
|
|
6353
|
+
this.isLoadingTargetAttributes.set(true);
|
|
6354
|
+
this.availableTargetAttributes.set([]);
|
|
6355
|
+
this.filteredTargetAttributes.set([]);
|
|
6356
|
+
try {
|
|
6357
|
+
// Load attributes from all target types and merge (union)
|
|
6358
|
+
const allAttrs = new Map();
|
|
6359
|
+
for (const ckTypeId of targetCkTypeIds) {
|
|
6360
|
+
const attrs = await firstValueFrom(this.dataService.fetchCkTypeAttributes(ckTypeId));
|
|
6361
|
+
for (const attr of attrs) {
|
|
6362
|
+
if (!allAttrs.has(attr.attributeName)) {
|
|
6363
|
+
allAttrs.set(attr.attributeName, attr);
|
|
6364
|
+
}
|
|
6365
|
+
}
|
|
6366
|
+
}
|
|
6367
|
+
const merged = Array.from(allAttrs.values()).sort((a, b) => a.attributeName.localeCompare(b.attributeName));
|
|
6368
|
+
this.availableTargetAttributes.set(merged);
|
|
6369
|
+
this.filteredTargetAttributes.set(merged);
|
|
6370
|
+
}
|
|
6371
|
+
catch (error) {
|
|
6372
|
+
console.error('Error loading target attributes:', error);
|
|
6373
|
+
}
|
|
6374
|
+
finally {
|
|
6375
|
+
this.isLoadingTargetAttributes.set(false);
|
|
6376
|
+
}
|
|
6377
|
+
}
|
|
6378
|
+
onEntityAttrFilterChange(filter) {
|
|
6379
|
+
this.entityAttrFilterText = filter.toLowerCase();
|
|
6380
|
+
this.updateFilteredEntityAttributes();
|
|
6381
|
+
}
|
|
6382
|
+
onTargetAttrFilterChange(filter) {
|
|
6383
|
+
this.targetAttrFilterText = filter.toLowerCase();
|
|
6384
|
+
this.updateFilteredTargetAttributes();
|
|
6385
|
+
}
|
|
6386
|
+
updateFilteredEntityAttributes() {
|
|
6387
|
+
const all = this.availableEntityAttributes();
|
|
6388
|
+
if (!this.entityAttrFilterText) {
|
|
6389
|
+
this.filteredEntityAttributes.set(all);
|
|
6390
|
+
}
|
|
6391
|
+
else {
|
|
6392
|
+
this.filteredEntityAttributes.set(all.filter(a => a.attributeName.toLowerCase().includes(this.entityAttrFilterText)));
|
|
6393
|
+
}
|
|
6394
|
+
}
|
|
6395
|
+
updateFilteredTargetAttributes() {
|
|
6396
|
+
const all = this.availableTargetAttributes();
|
|
6397
|
+
if (!this.targetAttrFilterText) {
|
|
6398
|
+
this.filteredTargetAttributes.set(all);
|
|
6399
|
+
}
|
|
6400
|
+
else {
|
|
6401
|
+
this.filteredTargetAttributes.set(all.filter(a => a.attributeName.toLowerCase().includes(this.targetAttrFilterText)));
|
|
6402
|
+
}
|
|
6049
6403
|
}
|
|
6050
6404
|
onSave() {
|
|
6051
6405
|
if (this.selectedCkType && this.selectedEntity) {
|
|
@@ -6055,7 +6409,9 @@ class AssociationsConfigDialogComponent {
|
|
|
6055
6409
|
showIncoming: this.showIncoming,
|
|
6056
6410
|
showOutgoing: this.showOutgoing,
|
|
6057
6411
|
roleFilter: this.selectedRoles.map(r => r.roleId),
|
|
6058
|
-
displayMode: this.displayMode
|
|
6412
|
+
displayMode: this.displayMode,
|
|
6413
|
+
entityAttributePaths: this.selectedEntityAttributes,
|
|
6414
|
+
targetAttributePaths: this.selectedTargetAttributes
|
|
6059
6415
|
});
|
|
6060
6416
|
}
|
|
6061
6417
|
}
|
|
@@ -6063,7 +6419,7 @@ class AssociationsConfigDialogComponent {
|
|
|
6063
6419
|
this.windowRef.close();
|
|
6064
6420
|
}
|
|
6065
6421
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: AssociationsConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6066
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: AssociationsConfigDialogComponent, isStandalone: true, selector: "mm-associations-config-dialog", inputs: { initialCkTypeId: "initialCkTypeId", initialRtId: "initialRtId", initialShowIncoming: "initialShowIncoming", initialShowOutgoing: "initialShowOutgoing", initialRoleFilter: "initialRoleFilter", initialDisplayMode: "initialDisplayMode" }, ngImport: i0, template: `
|
|
6422
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: AssociationsConfigDialogComponent, isStandalone: true, selector: "mm-associations-config-dialog", inputs: { initialCkTypeId: "initialCkTypeId", initialRtId: "initialRtId", initialShowIncoming: "initialShowIncoming", initialShowOutgoing: "initialShowOutgoing", initialRoleFilter: "initialRoleFilter", initialDisplayMode: "initialDisplayMode", initialEntityAttributePaths: "initialEntityAttributePaths", initialTargetAttributePaths: "initialTargetAttributePaths" }, ngImport: i0, template: `
|
|
6067
6423
|
<div class="config-container">
|
|
6068
6424
|
|
|
6069
6425
|
<div class="config-form" [class.loading]="isLoadingInitial">
|
|
@@ -6162,6 +6518,56 @@ class AssociationsConfigDialogComponent {
|
|
|
6162
6518
|
<p class="field-hint">How to display association targets.</p>
|
|
6163
6519
|
</div>
|
|
6164
6520
|
|
|
6521
|
+
<!-- Entity Attributes -->
|
|
6522
|
+
<div class="form-field" [class.disabled]="!selectedCkType || availableEntityAttributes().length === 0">
|
|
6523
|
+
<label>Entity Attributes (optional)</label>
|
|
6524
|
+
@if (isLoadingEntityAttributes()) {
|
|
6525
|
+
<div class="loading-roles">Loading attributes...</div>
|
|
6526
|
+
} @else if (availableEntityAttributes().length > 0) {
|
|
6527
|
+
<kendo-multiselect
|
|
6528
|
+
[data]="filteredEntityAttributes()"
|
|
6529
|
+
[textField]="'attributeName'"
|
|
6530
|
+
[valueField]="'attributeName'"
|
|
6531
|
+
[(ngModel)]="selectedEntityAttributes"
|
|
6532
|
+
[valuePrimitive]="true"
|
|
6533
|
+
placeholder="No attributes selected"
|
|
6534
|
+
[filterable]="true"
|
|
6535
|
+
(filterChange)="onEntityAttrFilterChange($event)">
|
|
6536
|
+
</kendo-multiselect>
|
|
6537
|
+
} @else {
|
|
6538
|
+
<kendo-textbox
|
|
6539
|
+
[disabled]="true"
|
|
6540
|
+
placeholder="No attributes available">
|
|
6541
|
+
</kendo-textbox>
|
|
6542
|
+
}
|
|
6543
|
+
<p class="field-hint">Attributes of the source entity to display below the header.</p>
|
|
6544
|
+
</div>
|
|
6545
|
+
|
|
6546
|
+
<!-- Target Attributes -->
|
|
6547
|
+
<div class="form-field" [class.disabled]="!selectedCkType || availableTargetAttributes().length === 0">
|
|
6548
|
+
<label>Target Attributes (optional)</label>
|
|
6549
|
+
@if (isLoadingTargetAttributes()) {
|
|
6550
|
+
<div class="loading-roles">Loading attributes...</div>
|
|
6551
|
+
} @else if (availableTargetAttributes().length > 0) {
|
|
6552
|
+
<kendo-multiselect
|
|
6553
|
+
[data]="filteredTargetAttributes()"
|
|
6554
|
+
[textField]="'attributeName'"
|
|
6555
|
+
[valueField]="'attributeName'"
|
|
6556
|
+
[(ngModel)]="selectedTargetAttributes"
|
|
6557
|
+
[valuePrimitive]="true"
|
|
6558
|
+
placeholder="No attributes selected"
|
|
6559
|
+
[filterable]="true"
|
|
6560
|
+
(filterChange)="onTargetAttrFilterChange($event)">
|
|
6561
|
+
</kendo-multiselect>
|
|
6562
|
+
} @else {
|
|
6563
|
+
<kendo-textbox
|
|
6564
|
+
[disabled]="true"
|
|
6565
|
+
placeholder="No target attributes available">
|
|
6566
|
+
</kendo-textbox>
|
|
6567
|
+
}
|
|
6568
|
+
<p class="field-hint">Attributes of target entities to display inline in the expanded list.</p>
|
|
6569
|
+
</div>
|
|
6570
|
+
|
|
6165
6571
|
<!-- Selection Preview -->
|
|
6166
6572
|
@if (selectedEntity) {
|
|
6167
6573
|
<div class="selection-preview">
|
|
@@ -6301,6 +6707,56 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
|
|
|
6301
6707
|
<p class="field-hint">How to display association targets.</p>
|
|
6302
6708
|
</div>
|
|
6303
6709
|
|
|
6710
|
+
<!-- Entity Attributes -->
|
|
6711
|
+
<div class="form-field" [class.disabled]="!selectedCkType || availableEntityAttributes().length === 0">
|
|
6712
|
+
<label>Entity Attributes (optional)</label>
|
|
6713
|
+
@if (isLoadingEntityAttributes()) {
|
|
6714
|
+
<div class="loading-roles">Loading attributes...</div>
|
|
6715
|
+
} @else if (availableEntityAttributes().length > 0) {
|
|
6716
|
+
<kendo-multiselect
|
|
6717
|
+
[data]="filteredEntityAttributes()"
|
|
6718
|
+
[textField]="'attributeName'"
|
|
6719
|
+
[valueField]="'attributeName'"
|
|
6720
|
+
[(ngModel)]="selectedEntityAttributes"
|
|
6721
|
+
[valuePrimitive]="true"
|
|
6722
|
+
placeholder="No attributes selected"
|
|
6723
|
+
[filterable]="true"
|
|
6724
|
+
(filterChange)="onEntityAttrFilterChange($event)">
|
|
6725
|
+
</kendo-multiselect>
|
|
6726
|
+
} @else {
|
|
6727
|
+
<kendo-textbox
|
|
6728
|
+
[disabled]="true"
|
|
6729
|
+
placeholder="No attributes available">
|
|
6730
|
+
</kendo-textbox>
|
|
6731
|
+
}
|
|
6732
|
+
<p class="field-hint">Attributes of the source entity to display below the header.</p>
|
|
6733
|
+
</div>
|
|
6734
|
+
|
|
6735
|
+
<!-- Target Attributes -->
|
|
6736
|
+
<div class="form-field" [class.disabled]="!selectedCkType || availableTargetAttributes().length === 0">
|
|
6737
|
+
<label>Target Attributes (optional)</label>
|
|
6738
|
+
@if (isLoadingTargetAttributes()) {
|
|
6739
|
+
<div class="loading-roles">Loading attributes...</div>
|
|
6740
|
+
} @else if (availableTargetAttributes().length > 0) {
|
|
6741
|
+
<kendo-multiselect
|
|
6742
|
+
[data]="filteredTargetAttributes()"
|
|
6743
|
+
[textField]="'attributeName'"
|
|
6744
|
+
[valueField]="'attributeName'"
|
|
6745
|
+
[(ngModel)]="selectedTargetAttributes"
|
|
6746
|
+
[valuePrimitive]="true"
|
|
6747
|
+
placeholder="No attributes selected"
|
|
6748
|
+
[filterable]="true"
|
|
6749
|
+
(filterChange)="onTargetAttrFilterChange($event)">
|
|
6750
|
+
</kendo-multiselect>
|
|
6751
|
+
} @else {
|
|
6752
|
+
<kendo-textbox
|
|
6753
|
+
[disabled]="true"
|
|
6754
|
+
placeholder="No target attributes available">
|
|
6755
|
+
</kendo-textbox>
|
|
6756
|
+
}
|
|
6757
|
+
<p class="field-hint">Attributes of target entities to display inline in the expanded list.</p>
|
|
6758
|
+
</div>
|
|
6759
|
+
|
|
6304
6760
|
<!-- Selection Preview -->
|
|
6305
6761
|
@if (selectedEntity) {
|
|
6306
6762
|
<div class="selection-preview">
|
|
@@ -6340,6 +6796,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
|
|
|
6340
6796
|
type: Input
|
|
6341
6797
|
}], initialDisplayMode: [{
|
|
6342
6798
|
type: Input
|
|
6799
|
+
}], initialEntityAttributePaths: [{
|
|
6800
|
+
type: Input
|
|
6801
|
+
}], initialTargetAttributePaths: [{
|
|
6802
|
+
type: Input
|
|
6343
6803
|
}] } });
|
|
6344
6804
|
|
|
6345
6805
|
/**
|
|
@@ -20361,7 +20821,9 @@ function registerDefaultWidgets(registry) {
|
|
|
20361
20821
|
initialShowIncoming: assocWidget.showIncoming,
|
|
20362
20822
|
initialShowOutgoing: assocWidget.showOutgoing,
|
|
20363
20823
|
initialRoleFilter: assocWidget.roleFilter,
|
|
20364
|
-
initialDisplayMode: assocWidget.displayMode
|
|
20824
|
+
initialDisplayMode: assocWidget.displayMode,
|
|
20825
|
+
initialEntityAttributePaths: assocWidget.entityAttributePaths,
|
|
20826
|
+
initialTargetAttributePaths: assocWidget.targetAttributePaths
|
|
20365
20827
|
};
|
|
20366
20828
|
},
|
|
20367
20829
|
applyConfigResult: (widget, result) => ({
|
|
@@ -20370,7 +20832,9 @@ function registerDefaultWidgets(registry) {
|
|
|
20370
20832
|
showIncoming: result.showIncoming,
|
|
20371
20833
|
showOutgoing: result.showOutgoing,
|
|
20372
20834
|
roleFilter: result.roleFilter,
|
|
20373
|
-
displayMode: result.displayMode
|
|
20835
|
+
displayMode: result.displayMode,
|
|
20836
|
+
entityAttributePaths: result.entityAttributePaths,
|
|
20837
|
+
targetAttributePaths: result.targetAttributePaths
|
|
20374
20838
|
}),
|
|
20375
20839
|
// SOLID: Factory function
|
|
20376
20840
|
createDefaultConfig: (base) => ({
|
|
@@ -20393,7 +20857,9 @@ function registerDefaultWidgets(registry) {
|
|
|
20393
20857
|
showOutgoing: widget.showOutgoing,
|
|
20394
20858
|
maxAssociations: widget.maxAssociations,
|
|
20395
20859
|
roleFilter: widget.roleFilter,
|
|
20396
|
-
displayMode: widget.displayMode
|
|
20860
|
+
displayMode: widget.displayMode,
|
|
20861
|
+
entityAttributePaths: widget.entityAttributePaths ?? [],
|
|
20862
|
+
targetAttributePaths: widget.targetAttributePaths ?? []
|
|
20397
20863
|
}
|
|
20398
20864
|
}),
|
|
20399
20865
|
// SOLID: Deserialization from persistence
|
|
@@ -20412,7 +20878,9 @@ function registerDefaultWidgets(registry) {
|
|
|
20412
20878
|
showOutgoing: config['showOutgoing'] ?? true,
|
|
20413
20879
|
maxAssociations: config['maxAssociations'] ?? 5,
|
|
20414
20880
|
roleFilter: config['roleFilter'],
|
|
20415
|
-
displayMode: config['displayMode']
|
|
20881
|
+
displayMode: config['displayMode'],
|
|
20882
|
+
entityAttributePaths: config['entityAttributePaths'] ?? [],
|
|
20883
|
+
targetAttributePaths: config['targetAttributePaths'] ?? []
|
|
20416
20884
|
};
|
|
20417
20885
|
}
|
|
20418
20886
|
});
|
|
@@ -22598,14 +23066,15 @@ class MeshBoardViewComponent {
|
|
|
22598
23066
|
*/
|
|
22599
23067
|
updateUrlWithRtId(rtId) {
|
|
22600
23068
|
const currentUrl = this.router.url;
|
|
22601
|
-
|
|
22602
|
-
if (
|
|
22603
|
-
// Replace the rtId
|
|
22604
|
-
const
|
|
23069
|
+
const hasRtIdParam = this.route.snapshot.paramMap.has('rtId');
|
|
23070
|
+
if (hasRtIdParam) {
|
|
23071
|
+
// Replace the last URL segment (the old rtId) with the new one
|
|
23072
|
+
const lastSlashIndex = currentUrl.lastIndexOf('/');
|
|
23073
|
+
const newUrl = currentUrl.substring(0, lastSlashIndex + 1) + rtId;
|
|
22605
23074
|
this.router.navigateByUrl(newUrl, { replaceUrl: true });
|
|
22606
23075
|
}
|
|
22607
|
-
else
|
|
22608
|
-
// Append the rtId
|
|
23076
|
+
else {
|
|
23077
|
+
// Append the rtId to the current URL
|
|
22609
23078
|
this.router.navigateByUrl(`${currentUrl}/${rtId}`, { replaceUrl: true });
|
|
22610
23079
|
}
|
|
22611
23080
|
}
|