diff --git a/src/ScadaLink.CentralUI/Components/Pages/Design/Templates.razor b/src/ScadaLink.CentralUI/Components/Pages/Design/Templates.razor index 0c6a4e1..6b794af 100644 --- a/src/ScadaLink.CentralUI/Components/Pages/Design/Templates.razor +++ b/src/ScadaLink.CentralUI/Components/Pages/Design/Templates.razor @@ -81,7 +81,7 @@
@@ -178,22 +178,13 @@ // 3. Template nodes with composition leaves. Derived templates are // slot-owned and reached via their parent's composition leaf — never - // shown as standalone tree nodes. + // shown as standalone tree nodes. Composition leaves recurse so a + // composite slot (e.g. Pump composed with TempSensor) reveals its own + // child slots when expanded. + var templatesById = _templates.ToDictionary(t => t.Id); foreach (var t in _templates.Where(t => !t.IsDerived).OrderBy(t => t.Name, StringComparer.OrdinalIgnoreCase)) { - var compChildren = t.Compositions - .OrderBy(c => c.InstanceName, StringComparer.OrdinalIgnoreCase) - .Select(c => new TmplNode( - Key: $"c:{c.Id}", - Kind: TmplNodeKind.Composition, - EntityId: c.Id, - Label: c.InstanceName, - ParentFolderId: null, - OwnerTemplateId: t.Id, - Template: null, - Composition: c, - Children: new List())) - .ToList(); + var compChildren = BuildCompositionLeaves(t, templatesById); var tNode = new TmplNode( Key: $"t:{t.Id}", @@ -220,6 +211,33 @@ _treeRoots = roots; } + // Recursive: each composition leaf's children are the composed-template's + // own composition leaves. Cascaded derived templates carry their slot + // compositions, so walking ComposedTemplateId surfaces the full nested + // structure. + private static List BuildCompositionLeaves(Template owner, IReadOnlyDictionary templatesById) + { + var result = new List(); + foreach (var c in owner.Compositions.OrderBy(c => c.InstanceName, StringComparer.OrdinalIgnoreCase)) + { + var nestedChildren = templatesById.TryGetValue(c.ComposedTemplateId, out var composed) + ? BuildCompositionLeaves(composed, templatesById) + : new List(); + + result.Add(new TmplNode( + Key: $"c:{c.Id}", + Kind: TmplNodeKind.Composition, + EntityId: c.Id, + Label: c.InstanceName, + ParentFolderId: null, + OwnerTemplateId: owner.Id, + Template: null, + Composition: c, + Children: nestedChildren)); + } + return result; + } + private static void SortChildren(List children) { children.Sort((a, b) =>