@meshmakers/octo-ui 3.4.320 → 3.4.330

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.
@@ -5,6 +5,7 @@ import { SVGIcon } from '@progress/kendo-svg-icons';
5
5
  import { AttributeItem, AttributeValueTypeDto as AttributeValueTypeDto$1, CkTypeSelectorItem as CkTypeSelectorItem$1, RtEntityDto, FieldFilterDto, SearchFilterDto, SortDto, CkModelDto, CkTypeDto, GraphDirectionDto, RtAssociationDto, FieldFilterOperatorsDto, GetCkTypesDtoGQL, RuntimeEntityItem, RuntimeEntitySelectDataSource, RuntimeEntityDialogDataSource } from '@meshmakers/octo-services';
6
6
  import { GridDataResult, CellClickEvent, RowArgs, PageChangeEvent, SelectionEvent } from '@progress/kendo-angular-grid';
7
7
  import { TreeItemDataTyped, TreeItemData, CommandItemExecuteEventArgs, CommandItem } from '@meshmakers/shared-services';
8
+ import * as _meshmakers_octo_ui from '@meshmakers/octo-ui';
8
9
  import { ControlValueAccessor, Validator, FormControl, AbstractControl, ValidationErrors } from '@angular/forms';
9
10
  import { AutoCompleteComponent, PopupSettings } from '@progress/kendo-angular-dropdowns';
10
11
  import { DataSourceTyped, ListViewComponent, HierarchyDataSourceBase, ListViewMessages, FetchDataOptions, FetchResultTyped, BaseTreeDetailComponent, NodeDroppedEvent, TreeComponent } from '@meshmakers/shared-ui';
@@ -12,6 +13,17 @@ import { State } from '@progress/kendo-data-query/dist/npm/state';
12
13
  import { Observable } from 'rxjs';
13
14
  import { Routes } from '@angular/router';
14
15
 
16
+ /**
17
+ * Helpers for the entity-selector suffix on navigation column paths (AB#4323):
18
+ * `nav.type[rtId=...]->attr`, `nav.type[wellKnownName=...]->attr` or
19
+ * `nav.type[attributeName=value]->attr`.
20
+ *
21
+ * The selector pins the exact target entity of a value navigation across an
22
+ * N-multiplicity association. It always attaches to the FIRST navigation type
23
+ * segment (the hop that crosses the association).
24
+ */
25
+ type EntitySelectorKind = 'rtId' | 'wellKnownName' | 'attribute';
26
+
15
27
  interface AttributeSelectorDialogData {
16
28
  rtCkTypeId: string;
17
29
  selectedAttributes?: string[];
@@ -23,6 +35,8 @@ interface AttributeSelectorDialogData {
23
35
  additionalAttributes?: AttributeItem[];
24
36
  /** When true, hides the navigation properties checkbox and max depth controls */
25
37
  hideNavigationControls?: boolean;
38
+ /** When true, value columns across inbound / N-multiplicity associations are offered (first-match semantics, AB#4323) */
39
+ includeManyNavigations?: boolean;
26
40
  /** When set, restricts the available attributes to only these attribute paths (filtered client-side after fetching) */
27
41
  attributePaths?: string[];
28
42
  }
@@ -44,6 +58,7 @@ declare class AttributeSelectorDialogComponent implements OnInit {
44
58
  protected readonly searchIcon: _progress_kendo_svg_icons.SVGIcon;
45
59
  protected readonly arrowUpIcon: _progress_kendo_svg_icons.SVGIcon;
46
60
  protected readonly arrowDownIcon: _progress_kendo_svg_icons.SVGIcon;
61
+ protected readonly pencilIcon: _progress_kendo_svg_icons.SVGIcon;
47
62
  private _data;
48
63
  get data(): AttributeSelectorDialogData;
49
64
  set data(value: AttributeSelectorDialogData);
@@ -56,7 +71,17 @@ declare class AttributeSelectorDialogComponent implements OnInit {
56
71
  selectedValueTypeFilter: AttributeValueTypeDto$1 | null;
57
72
  includeNavigationProperties: boolean;
58
73
  maxDepth: number | null;
74
+ includeManyNavigations: boolean;
59
75
  hideNavigationControls: boolean;
76
+ selectorEditorTarget: AttributeItem | null;
77
+ selectorEditorBasePath: string;
78
+ selectorKind: EntitySelectorKind;
79
+ selectorAttributeName: string;
80
+ selectorValue: string;
81
+ readonly selectorKindOptions: {
82
+ text: string;
83
+ value: EntitySelectorKind;
84
+ }[];
60
85
  private attributePathsSet;
61
86
  availableAttributes: AttributeItem[];
62
87
  selectedAttributes: AttributeItem[];
@@ -75,6 +100,14 @@ declare class AttributeSelectorDialogComponent implements OnInit {
75
100
  onSearchChange(value: string): void;
76
101
  onValueTypeFilterChange(_value: AttributeValueTypeDto$1 | null): void;
77
102
  onNavigationPropertiesChange(): void;
103
+ onIncludeManyNavigationsChange(): void;
104
+ hasNavigation(path: string): boolean;
105
+ openSelectorEditor(item: AttributeItem): void;
106
+ canApplySelector(): boolean;
107
+ applySelectorToTarget(): void;
108
+ removeSelectorFromTarget(): void;
109
+ closeSelectorEditor(): void;
110
+ private rewriteSelectedPath;
78
111
  onMaxDepthChange(value: number | null): void;
79
112
  addSelected(): void;
80
113
  removeSelected(): void;
@@ -450,9 +483,19 @@ interface AttributeItemLike {
450
483
  value?: unknown;
451
484
  }
452
485
  /**
453
- * Extracts the list of available data point names from an entity's attribute
454
- * list. Always includes {@link DEFAULT_DATA_POINT} first; named state entries
455
- * follow in alphabetical order.
486
+ * One data point of a source entity: its name plus when the record or the
487
+ * entity carries one its last known value. `currentValue` is `undefined`
488
+ * when unknown (never `null`-normalised, so a real null value stays visible).
489
+ */
490
+ interface DataPointInfo {
491
+ name: string;
492
+ currentValue?: unknown;
493
+ }
494
+ /**
495
+ * Extracts the available data points (name + last known value) from an
496
+ * entity's attribute list. Always includes {@link DEFAULT_DATA_POINT} first
497
+ * (carrying the entity's own `CurrentValue` attribute when present); named
498
+ * state entries follow in alphabetical order.
456
499
  *
457
500
  * Tolerates the three record shapes the platform produces for a RecordArray:
458
501
  *
@@ -460,8 +503,13 @@ interface AttributeItemLike {
460
503
  * 2. Pipeline / MongoDB: each record is `{ attributes: { Name: "…", ExternalId: "…" } }`.
461
504
  * 3. Flat: each record is `{ Name: "…", ExternalId: "…" }` (older dumps / sample data).
462
505
  *
463
- * Returns `[DEFAULT_DATA_POINT]` for null/undefined input, attributes without a
464
- * States/DataPoints entry, or malformed RecordArrays.
506
+ * Returns `[{ name: DEFAULT_DATA_POINT }]` for null/undefined input, attributes
507
+ * without a States/DataPoints entry, or malformed RecordArrays.
508
+ */
509
+ declare function extractDataPoints(attributes: readonly (AttributeItemLike | null | undefined)[] | null | undefined): DataPointInfo[];
510
+ /**
511
+ * Extracts the list of available data point names from an entity's attribute
512
+ * list — the name-only view of {@link extractDataPoints}.
465
513
  */
466
514
  declare function extractDataPointNames(attributes: readonly (AttributeItemLike | null | undefined)[] | null | undefined): string[];
467
515
 
@@ -502,8 +550,17 @@ declare class DataPointPickerComponent {
502
550
  /** Emits whenever the user enters a custom filter — useful for callers that
503
551
  * want to refine a backing catalogue independently of the value selection. */
504
552
  readonly filterChange: _angular_core.OutputEmitterRef<string>;
505
- protected readonly options: _angular_core.WritableSignal<string[]>;
553
+ /** Loaded data points incl. their last known values. */
554
+ private readonly dataPointInfos;
555
+ protected readonly options: _angular_core.Signal<string[]>;
506
556
  protected readonly loading: _angular_core.WritableSignal<boolean>;
557
+ /**
558
+ * Last known value of the currently selected data point, or undefined when
559
+ * the selection is a custom/unknown name or the source carries no value.
560
+ * Public so hosts (e.g. the mapping-edit dialog's expression preview) can
561
+ * read it off a template reference.
562
+ */
563
+ readonly currentValue: _angular_core.Signal<unknown>;
507
564
  /**
508
565
  * Current filter text entered into the combobox. Drives the case-insensitive
509
566
  * *contains* match below. Kendo's combobox by default does not filter the
@@ -539,7 +596,15 @@ declare class DataPointResolverService {
539
596
  items?: readonly (AttributeItemLike | null | undefined)[] | null;
540
597
  } | null;
541
598
  } | null | undefined): string[];
599
+ /** Like {@link extractFromEntity}, but keeps each data point's last known value. */
600
+ extractInfosFromEntity(entity: {
601
+ attributes?: {
602
+ items?: readonly (AttributeItemLike | null | undefined)[] | null;
603
+ } | null;
604
+ } | null | undefined): DataPointInfo[];
542
605
  load(rtId: string, ckTypeId: string): Promise<string[]>;
606
+ /** Like {@link load}, but keeps each data point's last known value. */
607
+ loadInfos(rtId: string, ckTypeId: string): Promise<DataPointInfo[]>;
543
608
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<DataPointResolverService, never>;
544
609
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<DataPointResolverService>;
545
610
  }
@@ -770,6 +835,12 @@ interface AssociationGroupNode {
770
835
  roleId: string;
771
836
  targetCkTypeId: string;
772
837
  direction: GraphDirectionDto;
838
+ /**
839
+ * rtId of the GRANDparent entity (the tree parent of the entity this group
840
+ * belongs to). Its targets exclude this entity so the node the user came from
841
+ * does not reappear inside the group (direct-parent back-edge suppression).
842
+ */
843
+ excludeRtId?: string;
773
844
  }
774
845
  declare class RuntimeBrowserDataSource extends OctoGraphQlHierarchyDataSource<BrowserItem$3> {
775
846
  private readonly getTreesDtoGQL;
@@ -792,12 +863,6 @@ declare class RuntimeBrowserDataSource extends OctoGraphQlHierarchyDataSource<Br
792
863
  * `fetchChildren()` to apply the root-level whitelist for `Type` perspectives.
793
864
  */
794
865
  private activePerspective;
795
- /**
796
- * rtIds of the current perspective's root nodes. The primary/secondary role
797
- * whitelist is applied ONLY to the direct children of these roots — deeper
798
- * nodes keep full auto-discovery (whitelist-at-root-only).
799
- */
800
- private readonly perspectiveRootRtIds;
801
866
  /**
802
867
  * Selects the active perspective by key. The host is responsible for reloading
803
868
  * the tree afterwards (e.g. `treeDetail.refreshTree()`), which re-runs
@@ -813,15 +878,25 @@ declare class RuntimeBrowserDataSource extends OctoGraphQlHierarchyDataSource<Br
813
878
  * entry, so the host can hide the switcher.
814
879
  */
815
880
  getPerspectives(): Promise<PerspectiveDefinition[]>;
881
+ /**
882
+ * The graph direction the active perspective navigates its root roles in
883
+ * (AB#4263). Defaults to Inbound (the containment side, as for auto-discovery);
884
+ * `primaryDirection: Outbound` is required when the association is authored on
885
+ * the root entity (e.g. DistributionSystem --SystemMembers--> members). Applied
886
+ * to the whole root whitelist (primary + secondary) for simplicity.
887
+ */
888
+ private perspectiveNavDirection;
816
889
  /**
817
890
  * The role-id whitelist of the active perspective (primary + secondary), or
818
891
  * null when the perspective is not a `Type` perspective or declares no roles.
819
892
  */
820
893
  private activePerspectiveWhitelist;
821
894
  /**
822
- * The set of role ids the given entity's direct children are restricted to,
823
- * or null for full auto-discovery. Non-null only when the entity is a root of
824
- * the active `Type` perspective (whitelist-at-root-only).
895
+ * The set of role ids the given tree item's direct children are restricted to,
896
+ * or null for full auto-discovery. Non-null only when the item is a TOP-LEVEL
897
+ * root of the active `Type` perspective (whitelist-at-root-only) — identified
898
+ * by its id prefix, so a root entity recurring deeper in the tree is NOT
899
+ * re-treated as a root (which would cycle).
825
900
  */
826
901
  private rootWhitelistFor;
827
902
  /**
@@ -840,17 +915,40 @@ declare class RuntimeBrowserDataSource extends OctoGraphQlHierarchyDataSource<Br
840
915
  * created fresh per browser/picker mount.
841
916
  */
842
917
  private readonly inboundRolesCache;
918
+ /**
919
+ * Same as inboundRolesCache but for OUTBOUND roles (AB#4263). A `Type`
920
+ * perspective whose association is authored on the root entity (e.g.
921
+ * EnergyIQ/DistributionSystem --SystemMembers--> members) reaches its members
922
+ * on the outbound side, so perspective roots may navigate outbound.
923
+ */
924
+ private readonly outboundRolesCache;
925
+ /**
926
+ * The rtId of the entity tree node each entity item was loaded under
927
+ * (association group nodes are transparent — they record the entity that owns
928
+ * the group). Used to suppress the direct parent's back-edge on expand: an
929
+ * edge traversed downwards (e.g. system --SystemMembers--> member) is the very
930
+ * same edge the child's opposite-direction auto-discovery finds again, which
931
+ * would render the parent as its own child's child. Keyed by item object
932
+ * identity (every fetch builds fresh DTOs), so entries are collected together
933
+ * with their tree nodes. Roots are never registered and get no exclusion.
934
+ */
935
+ private readonly parentEntityRtIds;
843
936
  private static readonly ckTypeMetaData;
844
937
  fetchChildren(item: TreeItemDataTyped<BrowserItem$3>): Promise<TreeItemDataTyped<BrowserItem$3>[]>;
845
938
  /**
846
- * Discovers the navigable inbound role groups of an entity from its ACTUAL
847
- * inbound edges (associations.definitions), grouped by (roleId, origin CK
848
- * type) with exact counts. Labels are enriched from the CK type schema
849
- * (friendly inbound navigation property name) when the role is declared on the
850
- * type, and otherwise derived from the role id (so orphan roles still get a
851
- * readable label).
852
- */
853
- private discoverEntityInboundRoleGroups;
939
+ * Discovers the navigable role groups of an entity from its ACTUAL edges
940
+ * (associations.definitions) in the given direction, grouped by (roleId, other-
941
+ * end CK type) with exact counts. For INBOUND the other end is the edge origin,
942
+ * for OUTBOUND it is the edge target (AB#4263 a perspective root may navigate
943
+ * outbound, e.g. DistributionSystem --SystemMembers--> members). Labels are
944
+ * enriched from the CK type schema (friendly navigation property name + base
945
+ * type) when the role is declared on the type, otherwise derived from the role
946
+ * id (so orphan roles still get a readable label). Edges whose other end is
947
+ * `excludeRtId` (the entity's direct tree parent) are skipped, so a group that
948
+ * only contains the parent's back-edge is dropped entirely and counts never
949
+ * include it.
950
+ */
951
+ private discoverEntityRoleGroups;
854
952
  /** Readable fallback label for a role id without a schema navigation name. */
855
953
  private deriveRoleLabel;
856
954
  /**
@@ -865,12 +963,22 @@ declare class RuntimeBrowserDataSource extends OctoGraphQlHierarchyDataSource<Br
865
963
  * schema and cached per type id for the lifetime of this data source.
866
964
  */
867
965
  private getInboundRoles;
966
+ /**
967
+ * Returns the OUTBOUND association roles of a CK type (AB#4263), discovered
968
+ * from the CK schema and cached per type id. Mirrors getInboundRoles but reads
969
+ * the `out` roles: for an OUTBOUND role this entity is the association's ORIGIN
970
+ * side, so the related entities to navigate to are on the TARGET side —
971
+ * targets(direction: OUTBOUND, ckId: rtTargetCkTypeId).
972
+ */
973
+ private getOutboundRoles;
868
974
  /** Loads the target entities reachable from an entity through one role. */
869
975
  private fetchAssociationTargets;
870
976
  /**
871
977
  * Builds tree items for runtime entities, de-duplicating by rtId and marking
872
978
  * an entity expandable when its CK type defines at least one inbound
873
- * association role.
979
+ * association role. `opts.excludeRtId` drops the direct parent's back-edge
980
+ * target; `opts.parentRtId` registers the built items' tree parent so their
981
+ * own expansion can exclude it in turn.
874
982
  */
875
983
  private buildEntityTreeItems;
876
984
  /** Stable tree-node id for an association group node. */
@@ -884,9 +992,10 @@ declare class RuntimeBrowserDataSource extends OctoGraphQlHierarchyDataSource<Br
884
992
  fetchRootNodes(): Promise<TreeItemDataTyped<BrowserItem$3>[]>;
885
993
  /**
886
994
  * Loads all runtime instances of a CK type as the roots of a `Type`
887
- * perspective (AB#4263), registering their rtIds so the whitelist applies to
888
- * their direct children only. Expandability is schema-based (does the type
889
- * declare an inbound role, restricted to the whitelist when set).
995
+ * perspective (AB#4263), tagging them with the root id prefix so the whitelist
996
+ * applies to their direct children only. Expandability is schema-based (does the type
997
+ * declare a role in the perspective's navigation direction, restricted to the
998
+ * whitelist when set).
890
999
  */
891
1000
  private fetchTypePerspectiveRoots;
892
1001
  /**
@@ -2700,6 +2809,63 @@ declare class RuntimeBrowserStateService {
2700
2809
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<RuntimeBrowserStateService>;
2701
2810
  }
2702
2811
 
2812
+ /** The per-tenant mapping coverage configuration as loaded for editing. */
2813
+ interface MappingCoverageConfig {
2814
+ /** rtId of the singleton, or null when it does not exist yet. */
2815
+ rtId: string | null;
2816
+ /** True when the CK type is installed on the tenant (System.UI >= 2.4.0). */
2817
+ typePresent: boolean;
2818
+ /** CK types whose entities act as source candidates in the Orphan Sources tab. */
2819
+ sourceCandidateCkTypeIds: string[];
2820
+ }
2821
+ /**
2822
+ * Loads and saves the optional per-tenant `System.UI/MappingCoverageConfiguration`
2823
+ * singleton that carries the source-catalogue CK types for the Mapping Coverage
2824
+ * page's Orphan Sources tab.
2825
+ *
2826
+ * Design notes (mirrors {@link TreeNavigationConfigService}):
2827
+ * - The config entity is OPTIONAL. When the CK type is not installed on the
2828
+ * tenant (System.UI < 2.4.0) or no instance exists, `loadConfig` reports
2829
+ * `typePresent`/an empty list and the host can fall back (e.g. to its old
2830
+ * localStorage persistence) without surfacing an error.
2831
+ * - The singleton field (`systemUIMappingCoverageConfiguration`) only exists in
2832
+ * the tenant schema when the CK type is installed, so querying it blindly
2833
+ * would raise a GraphQL validation error (and a user-facing toast). We first
2834
+ * probe the CK schema with the always-valid `constructionKit.types` query and
2835
+ * only run the singleton query when the type is present.
2836
+ * - Uses inline `gql` (not codegen) so the feature is decoupled from a schema
2837
+ * re-introspection that includes the new CK type.
2838
+ * - No session cache: the page loads once per visit / tenant switch, so every
2839
+ * `loadConfig` goes to the network and there is no reset-on-tenant-switch
2840
+ * pitfall.
2841
+ */
2842
+ declare class MappingCoverageConfigService {
2843
+ private readonly apollo;
2844
+ /**
2845
+ * Loads the singleton. Returns `typePresent: false` (and an empty list) when
2846
+ * the CK type is not installed on the tenant; `rtId: null` when the type is
2847
+ * installed but no instance exists yet.
2848
+ */
2849
+ loadConfig(): Promise<MappingCoverageConfig>;
2850
+ /**
2851
+ * Creates or updates the singleton with the given source CK types. Returns
2852
+ * the singleton rtId.
2853
+ */
2854
+ saveConfig(rtId: string | null, sourceCandidateCkTypeIds: string[]): Promise<string>;
2855
+ /**
2856
+ * Probes whether the `System.UI/MappingCoverageConfiguration` CK type is
2857
+ * installed on the tenant (System.UI >= 2.4.0). Uses the always-valid
2858
+ * `constructionKit.types` query so it never raises a schema validation error.
2859
+ */
2860
+ private probeTypePresent;
2861
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<MappingCoverageConfigService, never>;
2862
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<MappingCoverageConfigService>;
2863
+ }
2864
+ declare const MAPPING_COVERAGE_CONFIG_CONSTANTS: {
2865
+ CONFIG_CK_TYPE_ID: string;
2866
+ CONFIG_WELL_KNOWN_NAME: string;
2867
+ };
2868
+
2703
2869
  /**
2704
2870
  * View model for a DataPointMapping entity with resolved source/target information.
2705
2871
  * Used by the DataMappingOverviewComponent for display in the grid.
@@ -2872,6 +3038,28 @@ interface CoverageValidationDetail {
2872
3038
  missingRequired: string[];
2873
3039
  missingRecommended: string[];
2874
3040
  }
3041
+ /**
3042
+ * Descriptor of a synthetic association group node (AB#4262 port): groups all
3043
+ * entities reachable from a parent entity through ONE inbound association role
3044
+ * (e.g. `EnergyIQ/SpaceSensors` → "Sensors (5)"). Its target entities load
3045
+ * lazily on expand. Group nodes are not runtime entities — the detail panel
3046
+ * and mapping CRUD stay disabled for them.
3047
+ */
3048
+ interface CoverageGroupNodeInfo {
3049
+ /** Entity the grouped edges belong to (the group's tree parent). */
3050
+ parentRtId: string;
3051
+ parentCkTypeId: string;
3052
+ /** Runtime association role id navigated INBOUND from the parent. */
3053
+ roleId: string;
3054
+ /** CK type used as the `ckId` to load the targets (schema origin base). */
3055
+ targetCkTypeId: string;
3056
+ /**
3057
+ * rtId of the GRANDparent entity — excluded from the group's targets so the
3058
+ * node the user came from does not reappear inside the group (direct-parent
3059
+ * back-edge suppression, relevant under `Type` perspective roots).
3060
+ */
3061
+ excludeRtId?: string;
3062
+ }
2875
3063
  /**
2876
3064
  * Payload attached to every tree item in the coverage tree. Stored as the `item`
2877
3065
  * of a `TreeItemDataTyped<CoverageNodePayload>` so the host can render and react.
@@ -2887,6 +3075,12 @@ interface CoverageNodePayload extends CoverageEntityRef {
2887
3075
  validationStatus: CoverageNodeStatus | null;
2888
3076
  /** Full validation detail (missing/present lists) when loaded. */
2889
3077
  validationDetail: CoverageValidationDetail | null;
3078
+ /**
3079
+ * Set when this item is a synthetic association group node instead of a
3080
+ * runtime entity. `rtId` then holds the synthetic tree-node id and
3081
+ * `ckTypeId` is empty — consumers must not treat such an item as an entity.
3082
+ */
3083
+ associationGroup?: CoverageGroupNodeInfo;
2890
3084
  }
2891
3085
  type CoverageTreeItem = TreeItemDataTyped<CoverageNodePayload>;
2892
3086
  /**
@@ -2954,6 +3148,19 @@ interface OrphanCandidate {
2954
3148
  parentPath: OrphanCandidateParent[];
2955
3149
  }
2956
3150
 
3151
+ /**
3152
+ * Navigation override applied at the ROOT level when a `Type` tree perspective
3153
+ * (AB#4263) is active: the root's direct children are resolved through the
3154
+ * perspective's primary role/direction (e.g. `EnergyIQ/SystemMembers`,
3155
+ * OUTBOUND) instead of the spatial `childRoleId`. Deeper levels always fall
3156
+ * back to the spatial hierarchy (whitelist-at-root-only, mirroring the
3157
+ * runtime browser). The child CK type filter stays `config.childCkTypeId`
3158
+ * (polymorphic), so perspective members must derive from it.
3159
+ */
3160
+ interface CoveragePerspectiveNav {
3161
+ childRoleId: string;
3162
+ childDirection: GraphDirectionDto;
3163
+ }
2957
3164
  /**
2958
3165
  * Generic hierarchy data source for the Mapping Coverage Tree.
2959
3166
  *
@@ -2961,15 +3168,53 @@ interface OrphanCandidate {
2961
3168
  * `<mm-tree-view>` instance. The data source resolves child nodes through the
2962
3169
  * configured `childRoleId` / `childCkTypeId` and decorates each item with the
2963
3170
  * number of inbound mappings (via `mappingRoleId`).
3171
+ *
3172
+ * On top of the spatial hierarchy, every entity node auto-discovers its OTHER
3173
+ * inbound association roles from its actual edges (AB#4262 port from the
3174
+ * runtime browser): each such role becomes an expandable group node (e.g.
3175
+ * `Sensors (5)` via `EnergyIQ/SpaceSensors`) whose targets — with their own
3176
+ * mapping counts — load lazily on expand. The mapping roles themselves
3177
+ * (MapsTo/MapsFrom) and the spatial child role are excluded from discovery,
3178
+ * and the optional per-tenant `TreeNavigationConfiguration` overrides
3179
+ * (visible/displayName/sortIndex/grouped/icon) apply exactly as in the
3180
+ * runtime browser.
2964
3181
  */
2965
3182
  declare class MappingCoverageTreeDataSource extends HierarchyDataSourceBase<CoverageNodePayload> {
2966
3183
  private readonly getCoverageNodeGQL;
3184
+ private readonly getEntityAssociationsGQL;
3185
+ private readonly getCkTypeAssociationRolesGQL;
3186
+ private readonly treeNavConfig;
2967
3187
  private _root;
2968
3188
  private _config;
2969
3189
  private _rootMappingCount;
2970
3190
  private _validationMap;
3191
+ private _rootPerspectiveNav;
3192
+ /**
3193
+ * Cache of navigable inbound association roles per CK type id, discovered
3194
+ * lazily from the CK schema. Used to enrich discovered role groups with the
3195
+ * friendly navigation-property label + origin BASE type, and to decide
3196
+ * whether a child entity node is expandable beyond its spatial children.
3197
+ */
3198
+ private readonly inboundRolesCache;
3199
+ /**
3200
+ * The rtId of the entity tree node each entity payload was loaded under
3201
+ * (group nodes are transparent — they record the entity that owns the
3202
+ * group via `CoverageGroupNodeInfo.excludeRtId`). Used to suppress the
3203
+ * direct parent's back-edge on expand: the edge traversed downwards at a
3204
+ * `Type` perspective root (outbound) is the very same edge the child's
3205
+ * inbound auto-discovery finds again, which would render the parent as its
3206
+ * own child's child. Keyed by payload object identity (every fetch builds
3207
+ * fresh payloads), so entries are collected together with their tree nodes.
3208
+ */
3209
+ private readonly parentEntityRtIds;
2971
3210
  setRoot(root: CoverageEntityRef | null): void;
2972
3211
  setConfig(config: MappingCoverageTreeConfig): void;
3212
+ /**
3213
+ * Sets (or clears with null) the root-level navigation override of the
3214
+ * active `Type` perspective. The caller must reload the tree afterwards.
3215
+ */
3216
+ setRootPerspectiveNav(nav: CoveragePerspectiveNav | null): void;
3217
+ getRootPerspectiveNav(): CoveragePerspectiveNav | null;
2973
3218
  /**
2974
3219
  * Inject the validation report (rtId → status detail) loaded from the
2975
3220
  * latest PipelineExecution. Pass an empty map to clear the overlay.
@@ -2987,12 +3232,112 @@ declare class MappingCoverageTreeDataSource extends HierarchyDataSourceBase<Cove
2987
3232
  * collapsing the surrounding subtree.
2988
3233
  */
2989
3234
  refreshNode(rtId: string, ckTypeId: string): Promise<CoverageNodePayload | null>;
3235
+ /**
3236
+ * Builds the auto-discovered (non-spatial) part of an entity's child level:
3237
+ * one group node per inbound role found on its actual edges, plus the
3238
+ * flattened targets of roles a TreeNavigationConfiguration override marks as
3239
+ * `grouped: false`. Roles hidden by an override are dropped.
3240
+ */
3241
+ private buildDiscoveredNodes;
3242
+ /**
3243
+ * Discovers the navigable inbound role groups of an entity from its ACTUAL
3244
+ * edges (associations.definitions), grouped by roleId with exact counts.
3245
+ * This mirrors the runtime browser's AB#4262 discovery: real edges also
3246
+ * surface orphan roles (edge exists but the role is no longer declared on
3247
+ * the type in the installed CK model). Labels + the origin BASE type come
3248
+ * from the CK type schema when available; orphan roles fall back to the
3249
+ * concrete edge origin and a label derived from the role id.
3250
+ *
3251
+ * The spatial child role and the mapping roles (MapsTo/MapsFrom — rendered
3252
+ * in the detail panel, not the tree) are excluded, as are edges from the
3253
+ * direct tree parent (`excludeRtId`, back-edge suppression).
3254
+ */
3255
+ private discoverRoleGroups;
3256
+ /**
3257
+ * Association role ids never offered as tree navigation: the spatial child
3258
+ * role (already flattened into the hierarchy), the mapping roles (their
3259
+ * edges ARE the mapping counts / detail panel content) and the
3260
+ * pipeline-execution role.
3261
+ */
3262
+ private excludedNavigationRoleIds;
3263
+ /**
3264
+ * Returns the navigable inbound association roles of a CK type, discovered
3265
+ * from the CK schema and cached per type id for the lifetime of this data
3266
+ * source. Uses the RUNTIME ids from the backend (`rtRoleId`,
3267
+ * `rtOriginCkTypeId`), never the versioned fullName — for an INBOUND role
3268
+ * this entity is the association's TARGET side, so the related entities are
3269
+ * on the ORIGIN side.
3270
+ */
3271
+ private getInboundRoles;
3272
+ /**
3273
+ * Whether entities of the given type can have discoverable children beyond
3274
+ * the spatial hierarchy — i.e. the type declares at least one non-excluded
3275
+ * inbound role. Schema-based, so an expand arrow may open to an empty set
3276
+ * for an instance without edges (acceptable; mirrors the runtime browser).
3277
+ */
3278
+ private typeHasNavigableInboundRoles;
3279
+ /**
3280
+ * Builds tree items for entity children, de-duplicating by rtId and dropping
3281
+ * the direct parent's back-edge target (`excludeRtId`). An entity is marked
3282
+ * expandable when it has spatial grandchildren OR its CK type declares a
3283
+ * navigable inbound role. `parentRtId` registers each item's tree parent so
3284
+ * its own expansion can exclude it in turn.
3285
+ */
3286
+ private buildEntityItems;
3287
+ /** Builds the synthetic tree item for one discovered association role group. */
3288
+ private buildGroupItem;
2990
3289
  private buildItem;
2991
3290
  private queryNode;
2992
3291
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<MappingCoverageTreeDataSource, never>;
2993
3292
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<MappingCoverageTreeDataSource>;
2994
3293
  }
2995
3294
 
3295
+ /**
3296
+ * Pure helpers for the mapping-expression live preview shown in the mapping
3297
+ * edit dialogs: "value = <current source value> → <expression result>".
3298
+ *
3299
+ * The evaluator itself is provided by the HOST (Refinery Studio wires
3300
+ * `ExpressionEvaluatorService` from `@meshmakers/octo-process-diagrams`) — the
3301
+ * library stays evaluator-agnostic, mirroring the `expressionValidator` input
3302
+ * pattern of `DataMappingListComponent`. Note the evaluation is a CLIENT-SIDE
3303
+ * APPROXIMATION (expr-eval) of the backend's mXparser semantics; simple
3304
+ * arithmetic/comparison/ternary expressions behave identically.
3305
+ */
3306
+ /** Result of evaluating a mapping expression against a concrete value. */
3307
+ interface MappingExpressionPreviewResult {
3308
+ valid: boolean;
3309
+ error?: string;
3310
+ preview?: string;
3311
+ }
3312
+ /**
3313
+ * Host-provided evaluator: applies `expression` with the given `value` bound
3314
+ * to the `value` variable and returns the formatted result.
3315
+ */
3316
+ type MappingExpressionEvaluatorFn = (expression: string, value: unknown) => MappingExpressionPreviewResult;
3317
+ /**
3318
+ * Coerces a raw data-point value (often a string on the wire, e.g. "21.5" or
3319
+ * "true") into the type the expression should see — mirroring how the backend
3320
+ * feeds numeric state values into mXparser.
3321
+ */
3322
+ declare function coerceDataPointValue(raw: unknown): unknown;
3323
+ /** The computed preview line for the dialog. */
3324
+ interface ExpressionPreview {
3325
+ /** The raw source value shown as "value = …". */
3326
+ valueLabel: string;
3327
+ /** True when no expression is set — the value passes through unchanged. */
3328
+ passThrough: boolean;
3329
+ /**
3330
+ * Evaluation result, or null when an expression is set but the host
3331
+ * provided no evaluator (only the source value can be shown then).
3332
+ */
3333
+ result: MappingExpressionPreviewResult | null;
3334
+ }
3335
+ /**
3336
+ * Computes the preview line, or null when the source's current value is
3337
+ * unknown (nothing sensible to show).
3338
+ */
3339
+ declare function computeExpressionPreview(evaluator: MappingExpressionEvaluatorFn | null | undefined, expression: string | null | undefined, rawValue: unknown): ExpressionPreview | null;
3340
+
2996
3341
  interface RootCandidate {
2997
3342
  rtId: string;
2998
3343
  ckTypeId: string;
@@ -3003,18 +3348,57 @@ interface PipelineCandidate {
3003
3348
  rtId: string;
3004
3349
  ckTypeId: string;
3005
3350
  name: string;
3351
+ /**
3352
+ * Raw PipelineDefinition YAML (when the list query returned it). Used to
3353
+ * auto-detect the mapping backup pipelines by their node types
3354
+ * (ExportDataPointMappings@ / ImportDataPointMappings@) so the Export/Import
3355
+ * actions need no manual pipeline picking.
3356
+ */
3357
+ definition: string | null;
3006
3358
  }
3007
3359
  /**
3008
- * A bucket of orphan candidates sharing the same immediate parent. Produced
3009
- * by `orphanGroupedList` when the "group by parent" toggle is on.
3360
+ * Statistics object emitted by the ImportDataPointMappings@1 node (its
3361
+ * `statisticsTargetPath` output, returned via SetPipelineExecutionResult).
3362
+ * Mirrors the node's ImportStatistics record.
3363
+ */
3364
+ interface ImportStatistics {
3365
+ total?: number;
3366
+ resolved?: number;
3367
+ unresolved?: number;
3368
+ unresolvedEntries?: {
3369
+ name?: string;
3370
+ reason?: string;
3371
+ }[];
3372
+ }
3373
+ /**
3374
+ * A bucket of orphan candidates sharing the same parent NAME (not instance).
3375
+ * Produced by `orphanGroupedList` when the "group by parent" toggle is on.
3376
+ * Keying on the name merges same-named parents into one section — e.g. the
3377
+ * Loxone/Category "Beleuchtung" exists once per room as its own rtId, but the
3378
+ * user thinks of it as ONE category.
3010
3379
  */
3011
3380
  interface OrphanGroup {
3012
- /** Stable key — parent's CkTypeId@RtId, or `__no_parent__` for the catch-all bucket. */
3381
+ /** Stable key — the parent's name, or `__no_parent__` for the catch-all bucket. */
3013
3382
  key: string;
3014
3383
  /** Human-readable label rendered as the section heading. */
3015
3384
  label: string;
3016
3385
  items: OrphanCandidate[];
3017
3386
  }
3387
+ /**
3388
+ * Statistics object emitted by the GenerateDataPointMappings@1 node (its
3389
+ * `statisticsTargetPath` output, stored into PipelineExecution.OutputData via
3390
+ * SetPipelineExecutionResult). All fields optional — the UI shows whatever the
3391
+ * pipeline provided and falls back to a plain "completed" note otherwise.
3392
+ */
3393
+ interface GenerationStatistics {
3394
+ totalContainers?: number;
3395
+ matchedContainers?: number;
3396
+ unmatchedContainers?: number;
3397
+ unmatchedContainerNames?: string[];
3398
+ totalSuggestions?: number;
3399
+ ruleHits?: Record<string, number>;
3400
+ definedRuleIds?: string[];
3401
+ }
3018
3402
  /**
3019
3403
  * Master-detail component that visualises mapping coverage on a generic entity
3020
3404
  * hierarchy (defaults: Basic/Tree + Basic/TreeNode, mappings via
@@ -3024,9 +3408,11 @@ interface OrphanGroup {
3024
3408
  * Phase 1: tree + counts + read-only mapping list + CRUD (add new mapping,
3025
3409
  * relink source via {@link EntitySelectorDialogService}, delete).
3026
3410
  */
3027
- declare class MappingCoverageTreeComponent implements OnInit {
3411
+ declare class MappingCoverageTreeComponent implements OnInit, OnChanges {
3028
3412
  private readonly entitySelector;
3413
+ private readonly treeNavConfig;
3029
3414
  private readonly editDialog;
3415
+ private readonly bulkDialog;
3030
3416
  private readonly confirmation;
3031
3417
  private readonly communicationService;
3032
3418
  private readonly getEntitiesByCkType;
@@ -3050,6 +3436,13 @@ declare class MappingCoverageTreeComponent implements OnInit {
3050
3436
  * the pipeline externally.
3051
3437
  */
3052
3438
  tenantId: string | null;
3439
+ /**
3440
+ * Optional host-provided expression evaluator (e.g. expr-eval via
3441
+ * `@meshmakers/octo-process-diagrams`' ExpressionEvaluatorService). When
3442
+ * set, the mapping edit dialogs show a live preview of the expression
3443
+ * applied to the source data point's current value.
3444
+ */
3445
+ expressionEvaluator: MappingExpressionEvaluatorFn | null;
3053
3446
  readonly entitySelected: EventEmitter<CoverageEntityRef>;
3054
3447
  protected readonly icons: {
3055
3448
  refresh: _progress_kendo_svg_icons.SVGIcon;
@@ -3058,7 +3451,18 @@ declare class MappingCoverageTreeComponent implements OnInit {
3058
3451
  pencil: _progress_kendo_svg_icons.SVGIcon;
3059
3452
  trash: _progress_kendo_svg_icons.SVGIcon;
3060
3453
  link: _progress_kendo_svg_icons.SVGIcon;
3454
+ x: _progress_kendo_svg_icons.SVGIcon;
3455
+ download: _progress_kendo_svg_icons.SVGIcon;
3456
+ upload: _progress_kendo_svg_icons.SVGIcon;
3061
3457
  };
3458
+ /** Selectable tree perspectives (built-in Spatial + per-tenant configured). */
3459
+ protected readonly perspectives: _angular_core.WritableSignal<PerspectiveDefinition[]>;
3460
+ protected readonly activePerspectiveKey: _angular_core.WritableSignal<string>;
3461
+ /**
3462
+ * CK type whose instances populate the root dropdown: the active `Type`
3463
+ * perspective's rootCkTypeId, or the config default for Spatial.
3464
+ */
3465
+ protected readonly activeRootCkTypeId: _angular_core.Signal<string>;
3062
3466
  protected readonly rootCandidates: _angular_core.WritableSignal<RootCandidate[]>;
3063
3467
  protected readonly selectedRoot: _angular_core.WritableSignal<RootCandidate | null>;
3064
3468
  protected readonly selectedNode: _angular_core.WritableSignal<CoverageNodePayload | null>;
@@ -3072,6 +3476,24 @@ declare class MappingCoverageTreeComponent implements OnInit {
3072
3476
  protected readonly validationLoading: _angular_core.WritableSignal<boolean>;
3073
3477
  protected readonly validationError: _angular_core.WritableSignal<string | null>;
3074
3478
  protected readonly validationRunning: _angular_core.WritableSignal<boolean>;
3479
+ /** Generation: run a GenerateDataPointMappings-based pipeline from the UI. */
3480
+ protected readonly selectedGenerationPipeline: _angular_core.WritableSignal<PipelineCandidate | null>;
3481
+ protected readonly generationRunning: _angular_core.WritableSignal<boolean>;
3482
+ protected readonly generationError: _angular_core.WritableSignal<string | null>;
3483
+ protected readonly generationStats: _angular_core.WritableSignal<GenerationStatistics | null>;
3484
+ protected readonly generationCompletedAt: _angular_core.WritableSignal<string | null>;
3485
+ /**
3486
+ * Backup: export/import DataPointMappings via the mapping-backup pipelines
3487
+ * (ExportDataPointMappings@1 / ImportDataPointMappings@1 nodes). The
3488
+ * pipelines are auto-detected from the loaded pipeline definitions; the
3489
+ * Backup toolbar row only shows when at least one is deployed.
3490
+ */
3491
+ protected readonly exportBackupPipeline: _angular_core.Signal<PipelineCandidate | null>;
3492
+ protected readonly importBackupPipeline: _angular_core.Signal<PipelineCandidate | null>;
3493
+ protected readonly backupRunning: _angular_core.WritableSignal<"export" | "import" | null>;
3494
+ protected readonly backupError: _angular_core.WritableSignal<string | null>;
3495
+ protected readonly importStats: _angular_core.WritableSignal<ImportStatistics | null>;
3496
+ protected readonly importCompletedAt: _angular_core.WritableSignal<string | null>;
3075
3497
  /** Active tab: 'coverage' shows the tree, 'orphans' shows the unmapped sources. */
3076
3498
  protected readonly activeTab: _angular_core.WritableSignal<"coverage" | "orphans">;
3077
3499
  /** Source CK type currently inspected for orphans. */
@@ -3080,7 +3502,12 @@ declare class MappingCoverageTreeComponent implements OnInit {
3080
3502
  protected readonly orphanLoading: _angular_core.WritableSignal<boolean>;
3081
3503
  protected readonly orphanError: _angular_core.WritableSignal<string | null>;
3082
3504
  protected readonly orphanHideMapped: _angular_core.WritableSignal<boolean>;
3505
+ /** Full-text filter over the loaded orphan catalogue (client-side). */
3506
+ protected readonly orphanSearchText: _angular_core.WritableSignal<string>;
3083
3507
  protected readonly orphanFilteredList: _angular_core.Signal<OrphanCandidate[]>;
3508
+ /** rtIds of the orphan rows selected for the bulk "Map selected…" action. */
3509
+ protected readonly orphanSelectedIds: _angular_core.WritableSignal<ReadonlySet<string>>;
3510
+ protected readonly orphanSelectedCount: _angular_core.Signal<number>;
3084
3511
  protected readonly orphanStats: _angular_core.Signal<{
3085
3512
  total: number;
3086
3513
  unmapped: number;
@@ -3089,13 +3516,33 @@ declare class MappingCoverageTreeComponent implements OnInit {
3089
3516
  /**
3090
3517
  * Which parent CK type to group by, or null for a flat list. We let the user
3091
3518
  * pick the type instead of just "immediate parent" because Loxone-style trees
3092
- * include intermediate buckets (Loxone/Category) where each parent rtId is
3093
- * unique per room grouping by Category produces dozens of look-alike
3094
- * sections ("Stellantrieb" appears N times, once per room). The user almost
3095
- * always wants Loxone/Room or whichever level genuinely partitions the data,
3096
- * so we expose all parent types seen in the loaded data and let them choose.
3519
+ * include intermediate buckets (Loxone/Category) alongside the spatial level
3520
+ * (Loxone/Room); which one meaningfully partitions the data depends on the
3521
+ * task, so we expose all parent types seen in the loaded data and let them
3522
+ * choose. Groups are merged by parent NAME (see {@link orphanGroupedList}),
3523
+ * so per-room duplicates of the same category collapse into one section.
3097
3524
  */
3098
3525
  protected readonly orphanGroupParentType: _angular_core.WritableSignal<string | null>;
3526
+ /**
3527
+ * Multi-select filter over the group names of the active grouping type —
3528
+ * e.g. show only the "Beleuchtung" and "Klima" categories. Empty = no
3529
+ * filter. Cleared whenever the grouping type or the source type changes.
3530
+ */
3531
+ protected readonly orphanSelectedGroupNames: _angular_core.WritableSignal<string[]>;
3532
+ /**
3533
+ * Distinct group names available for the active grouping type, offered as
3534
+ * options in the group-name filter. Derived from ALL loaded candidates (not
3535
+ * the filtered list) so the option set stays stable while filters are
3536
+ * applied. The catch-all "(no parent…)" label sorts last.
3537
+ */
3538
+ protected readonly orphanAvailableGroupNames: _angular_core.Signal<string[]>;
3539
+ /**
3540
+ * The rows actually rendered (flat or grouped): the text/mapped filter
3541
+ * narrowed further by the group-name multi-select. Select-all and the
3542
+ * "N shown" badge operate on this list so bulk actions match what the user
3543
+ * sees.
3544
+ */
3545
+ protected readonly orphanVisibleList: _angular_core.Signal<OrphanCandidate[]>;
3099
3546
  /**
3100
3547
  * Distinct parent CK type ids found in the loaded candidates, sorted so the
3101
3548
  * deepest type (root-most ancestor) comes first. For Loxone-Controls this is
@@ -3106,6 +3553,29 @@ declare class MappingCoverageTreeComponent implements OnInit {
3106
3553
  protected readonly orphanGroupedList: _angular_core.Signal<OrphanGroup[]>;
3107
3554
  protected readonly summaryLine: _angular_core.Signal<string | null>;
3108
3555
  ngOnInit(): Promise<void>;
3556
+ /**
3557
+ * The Studio loads the per-tenant source-candidate CK types ASYNCHRONOUSLY
3558
+ * (System.UI singleton) and re-emits the `config` input when they arrive —
3559
+ * after this component's ngOnInit already ran with an empty list. Pick up
3560
+ * the late config here: refresh the data source and initialise the orphan
3561
+ * tab's type selection once, loading the catalogue immediately when the
3562
+ * tab is already active (otherwise it silently stays empty).
3563
+ */
3564
+ ngOnChanges(changes: SimpleChanges): void;
3565
+ /**
3566
+ * Loads the per-tenant tree perspectives (AB#4263) shared with the runtime
3567
+ * browser via {@link TreeNavigationConfigService} and prepends the built-in
3568
+ * Spatial one, de-duplicated by key (a configured `Spatial` overrides the
3569
+ * built-in). Failures degrade to the built-in perspective only.
3570
+ */
3571
+ private loadPerspectives;
3572
+ /**
3573
+ * Switches the active perspective: applies the root-level navigation
3574
+ * override on the data source (primary role + direction for `Type`
3575
+ * perspectives), clears the current selection and reloads the root
3576
+ * candidates from the perspective's root CK type.
3577
+ */
3578
+ protected onPerspectiveChange(key: string): Promise<void>;
3109
3579
  protected onPipelineSelectChange(rtId: string): void;
3110
3580
  protected refreshValidation(): Promise<void>;
3111
3581
  protected clearValidation(): void;
@@ -3114,6 +3584,7 @@ declare class MappingCoverageTreeComponent implements OnInit {
3114
3584
  protected refreshOrphans(): Promise<void>;
3115
3585
  protected toggleOrphanHideMapped(): void;
3116
3586
  protected onOrphanGroupParentTypeChange(value: string): void;
3587
+ protected onOrphanGroupNamesChange(names: string[]): void;
3117
3588
  /**
3118
3589
  * Returns the parent chain ordered for breadcrumb display: root-most ancestor
3119
3590
  * first, immediate parent last. `parentPath` itself is stored immediate-first
@@ -3126,6 +3597,10 @@ declare class MappingCoverageTreeComponent implements OnInit {
3126
3597
  * its inbound MapsFrom DataPointMapping count. The view filters the list
3127
3598
  * down to mappingCount === 0 by default, but the user can flip the toggle
3128
3599
  * to see all candidates (mapped + unmapped) for verification.
3600
+ *
3601
+ * Pages through the connection cursor until `hasNextPage` is false so
3602
+ * catalogues larger than one page are loaded completely (an earlier
3603
+ * version fetched a single 1000-row page and silently truncated).
3129
3604
  */
3130
3605
  private loadOrphanCandidates;
3131
3606
  /**
@@ -3136,16 +3611,83 @@ declare class MappingCoverageTreeComponent implements OnInit {
3136
3611
  */
3137
3612
  protected createMappingFromOrphan(orphan: OrphanCandidate): Promise<void>;
3138
3613
  protected trackOrphanByRtId(_index: number, item: OrphanCandidate): string;
3614
+ protected isOrphanSelected(rtId: string): boolean;
3615
+ protected toggleOrphanSelected(rtId: string): void;
3616
+ /** Selects every row currently visible under the active filter. */
3617
+ protected selectAllVisibleOrphans(): void;
3618
+ protected clearOrphanSelection(): void;
3619
+ /**
3620
+ * Opens the bulk mapping dialog for all selected orphan rows and, on
3621
+ * confirm, creates one DataPointMapping per source in a single atomic
3622
+ * CreateEntities mutation (shared target entity + paths + expression,
3623
+ * per-source MapsFrom association and generated name).
3624
+ */
3625
+ protected bulkMapSelected(): Promise<void>;
3139
3626
  /**
3140
3627
  * Triggers the selected validation pipeline on the Communication Controller
3141
3628
  * and, when it completes, automatically refreshes the coverage report so the
3142
3629
  * tree colour-codes update. Requires {@link tenantId} to be set.
3143
- *
3144
- * Polling strategy: every 1.5 s, fetch the latest execution metadata for the
3145
- * pipeline. When `dateTime` differs from the snapshot taken before the run,
3146
- * we know a new execution finished — refresh and stop. Aborts after 60 s.
3147
3630
  */
3148
3631
  protected runValidation(): Promise<void>;
3632
+ /**
3633
+ * Triggers the selected mapping-generation pipeline (GenerateDataPointMappings-
3634
+ * based auto-mapping) and, on completion, loads its statistics from the
3635
+ * execution's OutputData and refreshes the tree, the selected node's mapping
3636
+ * list and the orphan catalogue — all of which change when mappings are
3637
+ * created in bulk. Requires {@link tenantId} to be set.
3638
+ */
3639
+ protected runGeneration(): Promise<void>;
3640
+ protected onGenerationSelectChange(rtId: string): void;
3641
+ /**
3642
+ * Loads the latest execution's OutputData for the generation pipeline and
3643
+ * parses the GenerateDataPointMappings statistics from it (the pipeline is
3644
+ * expected to end with SetPipelineExecutionResult on its statisticsTargetPath).
3645
+ * Missing/foreign OutputData degrades to "completed without statistics".
3646
+ */
3647
+ private loadGenerationResult;
3648
+ /**
3649
+ * Loads the latest execution's OutputData + CompletedAt for any pipeline
3650
+ * (shared by the generation statistics and the backup export/import flows).
3651
+ */
3652
+ private loadLatestExecutionOutput;
3653
+ /**
3654
+ * Runs the auto-detected export pipeline (ExportDataPointMappings@1) via the
3655
+ * Communication Controller and offers the execution's OutputData — the
3656
+ * portable mapping export document — as a JSON file download.
3657
+ */
3658
+ protected exportMappings(): Promise<void>;
3659
+ /**
3660
+ * Handles the hidden file input: parses the selected export document and
3661
+ * runs the auto-detected import pipeline (ImportDataPointMappings@1) with
3662
+ * `{ body: document }` as the pipeline input — the same shape the pipeline's
3663
+ * HTTP POST trigger produces, so one definition serves both entry points.
3664
+ * On completion the import statistics (resolved/unresolved) are shown and
3665
+ * the tree, mapping list and orphan catalogue reload.
3666
+ */
3667
+ protected onImportFileSelected(event: Event): Promise<void>;
3668
+ /** Tooltip text for the "unresolved" badge — first 10 entry names + reason. */
3669
+ protected unresolvedImportNames(): string;
3670
+ /**
3671
+ * Offers a string as a JSON file download. Kept as a small overridable seam
3672
+ * so tests can intercept the browser download plumbing.
3673
+ */
3674
+ protected saveJsonFile(content: string, fileName: string): void;
3675
+ /**
3676
+ * Executes a pipeline on the Communication Controller and polls until a NEW
3677
+ * execution leaves the Running state.
3678
+ *
3679
+ * Polling strategy: snapshot the latest execution's id first; then every
3680
+ * 1.5 s fetch the latest execution metadata. When the id differs from the
3681
+ * snapshot AND the status is not Running, the run finished. Treats absent
3682
+ * status as "done": the in-memory debug-cache branch of
3683
+ * GET /pipelineDebug/.../latest doesn't populate Status (only the MongoDB
3684
+ * fallback does), so a sub-second pipeline that hits the cache path before
3685
+ * the DB row is visible would never satisfy a strict `status === 'Completed'`
3686
+ * check and the button would freeze. We only keep polling while the
3687
+ * controller *explicitly* reports "Running" (case-insensitive). Aborts after
3688
+ * 60 s ('timeout').
3689
+ */
3690
+ private executePipelineAndAwaitCompletion;
3149
3691
  private refreshTreeOverlay;
3150
3692
  private loadValidationPipelines;
3151
3693
  protected refreshRoots(): Promise<void>;
@@ -3176,7 +3718,7 @@ declare class MappingCoverageTreeComponent implements OnInit {
3176
3718
  */
3177
3719
  private resolveSourceNames;
3178
3720
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<MappingCoverageTreeComponent, never>;
3179
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<MappingCoverageTreeComponent, "mm-mapping-coverage-tree", never, { "config": { "alias": "config"; "required": false; }; "initialRoot": { "alias": "initialRoot"; "required": false; }; "tenantId": { "alias": "tenantId"; "required": false; }; }, { "entitySelected": "entitySelected"; }, never, never, true, never>;
3721
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<MappingCoverageTreeComponent, "mm-mapping-coverage-tree", never, { "config": { "alias": "config"; "required": false; }; "initialRoot": { "alias": "initialRoot"; "required": false; }; "tenantId": { "alias": "tenantId"; "required": false; }; "expressionEvaluator": { "alias": "expressionEvaluator"; "required": false; }; }, { "entitySelected": "entitySelected"; }, never, never, true, never>;
3180
3722
  }
3181
3723
 
3182
3724
  /**
@@ -3216,6 +3758,12 @@ interface MappingEditValue {
3216
3758
  interface MappingEditDialogData {
3217
3759
  mapping: MappingEditValue;
3218
3760
  title?: string;
3761
+ /**
3762
+ * Optional host-provided expression evaluator. When set, the dialog shows a
3763
+ * live preview of the mapping expression applied to the selected source
3764
+ * data point's current value.
3765
+ */
3766
+ expressionEvaluator?: MappingExpressionEvaluatorFn;
3219
3767
  /**
3220
3768
  * Default CK type id for the Target Attribute Path autocomplete when the
3221
3769
  * mapping has no target picked yet. Once the user picks a target entity,
@@ -3265,6 +3813,14 @@ declare class MappingEditDialogComponent {
3265
3813
  * legacy default. Drives the Target Attribute Path autocomplete dropdown.
3266
3814
  */
3267
3815
  protected readonly effectiveTargetCkTypeId: _angular_core.Signal<string | undefined>;
3816
+ /** The source data-point picker; carries the selected point's current value. */
3817
+ private readonly sourcePicker;
3818
+ /**
3819
+ * Live "value = X → Y" preview of the mapping expression applied to the
3820
+ * selected source data point's last known value. Null when the value is
3821
+ * unknown; the evaluation itself requires a host-provided evaluator.
3822
+ */
3823
+ protected readonly expressionPreview: _angular_core.Signal<_meshmakers_octo_ui.ExpressionPreview | null>;
3268
3824
  initialise(data: MappingEditDialogData): void;
3269
3825
  /**
3270
3826
  * Enabled when at least one labelled side of the mapping is filled enough
@@ -3286,6 +3842,7 @@ declare class MappingEditDialogComponent {
3286
3842
  protected pickSource(): Promise<void>;
3287
3843
  protected pickTarget(): Promise<void>;
3288
3844
  protected onSourceAttributePathChange(value: string): void;
3845
+ protected onExpressionChange(value: string): void;
3289
3846
  protected onTargetAttributePathChange(value: string | null): void;
3290
3847
  protected onTargetAttributeFilter(filter: string): void;
3291
3848
  protected onSave(): void;
@@ -3324,6 +3881,101 @@ declare class MappingEditDialogService {
3324
3881
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<MappingEditDialogService>;
3325
3882
  }
3326
3883
 
3884
+ /**
3885
+ * Shared settings applied to every mapping created by the bulk flow: one
3886
+ * target entity plus one source data point / target attribute / expression for
3887
+ * all selected sources. Per-source values (the MapsFrom association and the
3888
+ * generated name) are derived by the caller.
3889
+ */
3890
+ interface BulkMappingValue {
3891
+ targetRtId: string;
3892
+ targetCkTypeId: string;
3893
+ targetName?: string;
3894
+ sourceAttributePath: string;
3895
+ targetAttributePath: string;
3896
+ mappingExpression: string;
3897
+ enabled: boolean;
3898
+ }
3899
+ interface BulkMappingDialogData {
3900
+ /** The selected source entities (orphan-tab multi-select). */
3901
+ sources: CoverageEntityRef[];
3902
+ title?: string;
3903
+ /**
3904
+ * Optional host-provided expression evaluator. When set, the dialog shows a
3905
+ * live preview of the mapping expression applied to the FIRST selected
3906
+ * source's current data-point value.
3907
+ */
3908
+ expressionEvaluator?: MappingExpressionEvaluatorFn;
3909
+ }
3910
+ type BulkMappingDialogResult = {
3911
+ confirmed: true;
3912
+ value: BulkMappingValue;
3913
+ } | {
3914
+ confirmed: false;
3915
+ };
3916
+ /**
3917
+ * Bulk variant of the mapping editor: creates the SAME mapping shape (source
3918
+ * data point → target attribute, one shared target entity) for N selected
3919
+ * source entities in one go. The source data-point options are loaded from the
3920
+ * FIRST selected source — the usual bulk case is a set of same-typed controls
3921
+ * exposing identical states; free text stays possible for mixed sets.
3922
+ */
3923
+ declare class BulkMappingDialogComponent {
3924
+ private readonly windowRef;
3925
+ private readonly entitySelector;
3926
+ private readonly attributeSelectorDialog;
3927
+ private readonly attributeService;
3928
+ protected readonly browseIcon: _progress_kendo_svg_icons.SVGIcon;
3929
+ protected readonly sources: _angular_core.WritableSignal<CoverageEntityRef[]>;
3930
+ protected readonly firstSource: _angular_core.Signal<CoverageEntityRef>;
3931
+ protected readonly sourcePreview: _angular_core.Signal<string>;
3932
+ protected enabled: boolean;
3933
+ protected readonly sourceAttributePath: _angular_core.WritableSignal<string>;
3934
+ protected readonly mappingExpression: _angular_core.WritableSignal<string>;
3935
+ protected readonly targetRtId: _angular_core.WritableSignal<string | null>;
3936
+ protected readonly targetCkTypeId: _angular_core.WritableSignal<string | null>;
3937
+ protected readonly targetName: _angular_core.WritableSignal<string | null>;
3938
+ protected readonly targetAttributePath: _angular_core.WritableSignal<string>;
3939
+ private readonly targetAttributes;
3940
+ protected readonly targetFilter: _angular_core.WritableSignal<string>;
3941
+ protected readonly targetAttributesLoading: _angular_core.WritableSignal<boolean>;
3942
+ /** The source data-point picker; carries the selected point's current value. */
3943
+ private readonly sourcePicker;
3944
+ /** Optional host-provided evaluator (set via initialise). */
3945
+ private expressionEvaluator;
3946
+ /**
3947
+ * Live "value = X → Y" preview of the shared mapping expression applied to
3948
+ * the FIRST selected source's data-point value (the usual bulk case is a
3949
+ * set of same-typed controls, so the first one is representative).
3950
+ */
3951
+ protected readonly expressionPreview: _angular_core.Signal<_meshmakers_octo_ui.ExpressionPreview | null>;
3952
+ protected readonly targetAttributeList: _angular_core.Signal<AttributeItem[]>;
3953
+ initialise(data: BulkMappingDialogData): void;
3954
+ protected isValid(): boolean;
3955
+ protected pickTarget(): Promise<void>;
3956
+ protected onTargetAttributePathChange(value: string | null): void;
3957
+ protected browseTargetAttribute(): Promise<void>;
3958
+ protected onSave(): void;
3959
+ protected onCancel(): void;
3960
+ /** Direct CK attributes of the target type for the combobox (same rules as the single-mapping editor). */
3961
+ private loadTargetAttributes;
3962
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BulkMappingDialogComponent, never>;
3963
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BulkMappingDialogComponent, "mm-bulk-mapping-dialog", never, {}, {}, never, never, true, never>;
3964
+ }
3965
+
3966
+ declare class BulkMappingDialogService {
3967
+ private readonly windowService;
3968
+ private readonly windowStateService;
3969
+ /**
3970
+ * Opens the bulk mapping editor for the given source entities and returns
3971
+ * the shared mapping settings on confirm. The host creates the actual
3972
+ * mapping entities (one per source) — the dialog only collects the values.
3973
+ */
3974
+ open(data: BulkMappingDialogData): Promise<BulkMappingDialogResult>;
3975
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BulkMappingDialogService, never>;
3976
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<BulkMappingDialogService>;
3977
+ }
3978
+
3327
3979
  /**
3328
3980
  * Input data for the RuntimeEntityVariableDialog.
3329
3981
  */
@@ -3442,5 +4094,5 @@ declare class TenantSwitcherComponent {
3442
4094
  */
3443
4095
  declare function provideOctoUi(): EnvironmentProviders;
3444
4096
 
3445
- export { AssociationValidationService, AttributeSelectorDialogComponent, AttributeSelectorDialogService, AttributeSortSelectorDialogComponent, AttributeSortSelectorDialogService, AttributeValueTypeDto, CkTypeSelectorDialogComponent, CkTypeSelectorDialogService, CkTypeSelectorInputComponent, DEFAULT_CK_TYPE_SELECTOR_DIALOG_MESSAGES, DEFAULT_CK_TYPE_SELECTOR_INPUT_MESSAGES, DEFAULT_DATA_POINT, DEFAULT_MAPPING_COVERAGE_TREE_CONFIG, DEFAULT_PROPERTY_GRID_MESSAGES, DEFAULT_RUNTIME_BROWSER_MESSAGES, DataMappingOverviewComponent, DataPointPickerComponent, DataPointResolverService, DefaultPropertyCategory, EntityDetailComponent, EntityIdInfoComponent, EntitySelectorDialogComponent, EntitySelectorDialogService, FieldFilterEditorComponent, MappingCoverageTreeComponent, MappingCoverageTreeDataSource, MappingEditDialogComponent, MappingEditDialogService, OctoGraphQlDataSource, OctoGraphQlHierarchyDataSource, OctoLoaderComponent, PropertyConverterService, PropertyDisplayMode, PropertyGridComponent, PropertyValueDisplayComponent, RUNTIME_BROWSER_MESSAGES, RecordDetailDialogComponent, RtEntityIdHelper, RuntimeBrowserComponent, RuntimeBrowserOutletComponent, RuntimeBrowserPageComponent, RuntimeBrowserStateService, RuntimeEntityVariableDialogComponent, RuntimeEntityVariableDialogService, TREE_NAVIGATION_CONFIG_CONSTANTS, TenantSwitcherComponent, TreeNavigationConfigService, account_tree, add, analytics, app_registration, article, botService, category, chat, checklist, code, component_exchange, computer, createRuntimeBrowserRoutes, customer, dashboard, event_list, extractDataPointNames, graphic_eq, group, identityService, insert_link, manage_accounts, more_time, notifications, page_info, pages, person_search, playlist_add_check, pool, power, provideOctoUi, publicIcon, query_builder, schedule_send, settings, sort, storage, swagger, swagger_asset, swagger_bot, swagger_communication, swagger_identity, team_dashboard, tenancy, text_snippet, travel_explore, user_diagnostics, webhook, work };
3446
- export type { AttributeItemLike, AttributeSelectorDialogData, AttributeSelectorDialogResult, AttributeSelectorResult, AttributeSortItem, AttributeSortSelectorDialogData, AttributeSortSelectorDialogResult, AttributeSortSelectorResult, BinaryDownloadEvent, BrowserItem, BrowserState, CkAssociationRole, CkTypeSelectorDialogData, CkTypeSelectorDialogMessages, CkTypeSelectorDialogResult, CkTypeSelectorInputMessages, CkTypeSelectorResult, CoverageEntityRef, CoverageMappingItem, CoverageNodePayload, CoverageTreeIcons, CoverageTreeItem, DataPointMappingItem, DataPointMappingOverviewItem, EntitySelectorDialogData, EntitySelectorDialogResult, EntitySelectorResult, ExpressionValidationResult, ExpressionValidatorFn, FieldFilterItem, FilterVariable, MappingCoverageTreeConfig, MappingEditDialogData, MappingEditDialogResult, MappingEditValue, MappingOverviewSummary, MoveValidationResult, PerspectiveDefinition, PerspectiveDirection, PerspectiveRootMode, PropertyChangeEvent, PropertyGridConfig, PropertyGridItem, PropertyGridMessages, RtEntityId, RuntimeBrowserMessages, RuntimeBrowserRouteOptions, RuntimeEntityVariableDialogData, RuntimeEntityVariableDialogResult, RuntimeEntityVariableMapping, RuntimeEntityVariableResult, SortOption, TreeNavigationConfig, TreeNavigationRoleConfig, TreeNavigationRoleOverride, ValidationMessage };
4097
+ export { AssociationValidationService, AttributeSelectorDialogComponent, AttributeSelectorDialogService, AttributeSortSelectorDialogComponent, AttributeSortSelectorDialogService, AttributeValueTypeDto, BulkMappingDialogComponent, BulkMappingDialogService, CkTypeSelectorDialogComponent, CkTypeSelectorDialogService, CkTypeSelectorInputComponent, DEFAULT_CK_TYPE_SELECTOR_DIALOG_MESSAGES, DEFAULT_CK_TYPE_SELECTOR_INPUT_MESSAGES, DEFAULT_DATA_POINT, DEFAULT_MAPPING_COVERAGE_TREE_CONFIG, DEFAULT_PROPERTY_GRID_MESSAGES, DEFAULT_RUNTIME_BROWSER_MESSAGES, DataMappingOverviewComponent, DataPointPickerComponent, DataPointResolverService, DefaultPropertyCategory, EntityDetailComponent, EntityIdInfoComponent, EntitySelectorDialogComponent, EntitySelectorDialogService, FieldFilterEditorComponent, MAPPING_COVERAGE_CONFIG_CONSTANTS, MappingCoverageConfigService, MappingCoverageTreeComponent, MappingCoverageTreeDataSource, MappingEditDialogComponent, MappingEditDialogService, OctoGraphQlDataSource, OctoGraphQlHierarchyDataSource, OctoLoaderComponent, PropertyConverterService, PropertyDisplayMode, PropertyGridComponent, PropertyValueDisplayComponent, RUNTIME_BROWSER_MESSAGES, RecordDetailDialogComponent, RtEntityIdHelper, RuntimeBrowserComponent, RuntimeBrowserOutletComponent, RuntimeBrowserPageComponent, RuntimeBrowserStateService, RuntimeEntityVariableDialogComponent, RuntimeEntityVariableDialogService, TREE_NAVIGATION_CONFIG_CONSTANTS, TenantSwitcherComponent, TreeNavigationConfigService, account_tree, add, analytics, app_registration, article, botService, category, chat, checklist, code, coerceDataPointValue, component_exchange, computeExpressionPreview, computer, createRuntimeBrowserRoutes, customer, dashboard, event_list, extractDataPointNames, extractDataPoints, graphic_eq, group, identityService, insert_link, manage_accounts, more_time, notifications, page_info, pages, person_search, playlist_add_check, pool, power, provideOctoUi, publicIcon, query_builder, schedule_send, settings, sort, storage, swagger, swagger_asset, swagger_bot, swagger_communication, swagger_identity, team_dashboard, tenancy, text_snippet, travel_explore, user_diagnostics, webhook, work };
4098
+ export type { AttributeItemLike, AttributeSelectorDialogData, AttributeSelectorDialogResult, AttributeSelectorResult, AttributeSortItem, AttributeSortSelectorDialogData, AttributeSortSelectorDialogResult, AttributeSortSelectorResult, BinaryDownloadEvent, BrowserItem, BrowserState, BulkMappingDialogData, BulkMappingDialogResult, BulkMappingValue, CkAssociationRole, CkTypeSelectorDialogData, CkTypeSelectorDialogMessages, CkTypeSelectorDialogResult, CkTypeSelectorInputMessages, CkTypeSelectorResult, CoverageEntityRef, CoverageMappingItem, CoverageNodePayload, CoverageTreeIcons, CoverageTreeItem, DataPointInfo, DataPointMappingItem, DataPointMappingOverviewItem, EntitySelectorDialogData, EntitySelectorDialogResult, EntitySelectorResult, ExpressionPreview, ExpressionValidationResult, ExpressionValidatorFn, FieldFilterItem, FilterVariable, MappingCoverageConfig, MappingCoverageTreeConfig, MappingEditDialogData, MappingEditDialogResult, MappingEditValue, MappingExpressionEvaluatorFn, MappingExpressionPreviewResult, MappingOverviewSummary, MoveValidationResult, PerspectiveDefinition, PerspectiveDirection, PerspectiveRootMode, PropertyChangeEvent, PropertyGridConfig, PropertyGridItem, PropertyGridMessages, RtEntityId, RuntimeBrowserMessages, RuntimeBrowserRouteOptions, RuntimeEntityVariableDialogData, RuntimeEntityVariableDialogResult, RuntimeEntityVariableMapping, RuntimeEntityVariableResult, SortOption, TreeNavigationConfig, TreeNavigationRoleConfig, TreeNavigationRoleOverride, ValidationMessage };