Files
ScadaBridge/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/TemplateTreeNode.cs
T
Joseph Doherty 1f261263b2 fix(centralui): surface inherited compositions in the templates tree (followup #9)
The templates tree rendered a derived/composed member (e.g. LeftReactorSide,
derived from ReactorSide) as a flat leaf, omitting compositions it inherits
from its base (e.g. LeakTest composed onto ReactorSide). BuildCompositionLeavesFor
recursed only over a template's OWN composition rows; an inherited composition
row lives on the ancestor, and TemplateComposition has no IsInherited placeholder
(unlike attributes/alarms/scripts/native-sources), so the child's own Compositions
was empty. Same 'derived templates don't surface inherited members' family as
followups #1/#2, but for compositions. Deploy/flatten was always correct
(TemplateResolver.ResolveAllMembers walks the chain) — display-only.

Fix:
- BuildCompositionLeavesFor now renders the EFFECTIVE composition set (own +
  inherited) via EffectiveCompositionsFor, which walks the inheritance chain
  (leaf->root, child wins on InstanceName), mirroring the resolver.
- Inherited slots are flagged (TemplateTreeNode.IsInherited), badged 'inherited'
  in the label, and their context menu offers only 'Open composed template'
  (Rename/Delete edit the ancestor's slot, so suppressed on inherited nodes).
- The same inherited row can appear under several derived members (LeakTest under
  both LeftReactorSide and RightReactorSide), so composition nodes use a
  path-qualified KeyOverride to keep TreeView selection/expansion keys unique;
  recursion is cycle-guarded.

Tests: +1 bUnit (TemplatesPageTests.Renders_InheritedComposition_UnderDerivedComposedMember);
CentralUI suite 867 green; full solution builds 0/0.

Docs: Component-CentralUI.md (effective composition set in tree); known-issues
tracker #9 recorded + resolved.

Note: CentralUI change — shows on wonder-app-vd03 only after that host is redeployed.
2026-06-24 19:29:48 -04:00

61 lines
2.7 KiB
C#

namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
public enum TemplateTreeNodeKind
{
Folder,
Template,
/// <summary>
/// Composition slot under a parent Template — produced only by callers that
/// supply <c>TemplateFolderTree.ExtraTemplateChildren</c>. The Transport
/// Export wizard intentionally never emits this kind (compositions aren't
/// independently exportable); the Templates page uses it to surface slots.
/// </summary>
Composition,
}
/// <summary>
/// Adapter node used by <c>TemplateFolderTree</c> to model the template-folder
/// hierarchy in a TreeView. Folder nodes carry sub-folders + their templates as
/// children; template nodes are leaves unless the caller injects extras via
/// <c>TemplateFolderTree.ExtraTemplateChildren</c> (e.g. composition slots).
/// </summary>
public sealed class TemplateTreeNode
{
/// <summary>Discriminator indicating whether this node represents a folder, template, or composition slot.</summary>
public required TemplateTreeNodeKind Kind { get; init; }
/// <summary>Database id of the underlying folder, template, or composition record.</summary>
public required int Id { get; init; }
/// <summary>Display name of the node.</summary>
public required string Name { get; init; }
/// <summary>Child nodes (sub-folders, templates, or composition slots).</summary>
public List<TemplateTreeNode> Children { get; } = new();
/// <summary>
/// True when this composition node is <em>inherited</em> — its underlying
/// <c>TemplateComposition</c> row belongs to an ancestor of the template it is
/// rendered under, not to that template itself (followup #9). Inherited nodes are
/// badged read-only and their context menu suppresses Rename/Delete (those edit the
/// base). Always false for folders and templates.
/// </summary>
public bool IsInherited { get; init; }
/// <summary>
/// Explicit, path-qualified key override. The same inherited composition row can
/// surface under several derived members (e.g. LeakTest under both LeftReactorSide
/// and RightReactorSide), so a plain <c>c:{Id}</c> key would collide and break the
/// TreeView's selection/expansion tracking. Composition nodes set this to a
/// parent-path-qualified value; folders/templates leave it null and use the default.
/// </summary>
public string? KeyOverride { get; init; }
/// <summary>Stable key for TreeView selection / expansion tracking.</summary>
public string Key => KeyOverride ?? Kind switch
{
TemplateTreeNodeKind.Folder => $"f:{Id}",
TemplateTreeNodeKind.Template => $"t:{Id}",
TemplateTreeNodeKind.Composition => $"c:{Id}",
_ => $"x:{Id}",
};
}