diff --git a/src/ScadaLink.CentralUI/Components/Pages/Design/Templates.razor b/src/ScadaLink.CentralUI/Components/Pages/Design/Templates.razor index b246314..a4c307c 100644 --- a/src/ScadaLink.CentralUI/Components/Pages/Design/Templates.razor +++ b/src/ScadaLink.CentralUI/Components/Pages/Design/Templates.razor @@ -27,181 +27,56 @@ {
@_errorMessage
} - else if (_selectedTemplate == null) - { - @* Template list view *@ -
-

Templates

- -
- - - - @node.Template.Name - @if (node.Template.ParentTemplateId.HasValue) - { - inherits @(_templates.FirstOrDefault(t => t.Id == node.Template.ParentTemplateId)?.Name) - } - @if (!string.IsNullOrEmpty(node.Template.Description)) - { - @node.Template.Description - } - - @node.Template.Attributes.Count attr, @node.Template.Alarms.Count alm, @node.Template.Scripts.Count scr - - @if (node.Template.Compositions.Count > 0) - { - @node.Template.Compositions.Count comp - } - - - - - - - - No templates. Create one to get started. - - - } else { - @* Template detail/edit view *@ -
-
- -

@_selectedTemplate.Name

- @if (_selectedTemplate.ParentTemplateId.HasValue) - { - inherits @(_templates.FirstOrDefault(t => t.Id == _selectedTemplate.ParentTemplateId)?.Name) - } -
-
- -
-
- - @* Validation results *@ - @if (_validationResult != null) - { -
- @if (_validationResult.Errors.Count > 0) - { -
- Validation Errors (@_validationResult.Errors.Count) - -
- } - @if (_validationResult.Warnings.Count > 0) - { -
- Warnings (@_validationResult.Warnings.Count) - -
- } - @if (_validationResult.Errors.Count == 0 && _validationResult.Warnings.Count == 0) - { -
Validation passed with no errors or warnings.
- } -
- } - - @* Template info edit *@ -
-
Template Properties
-
-
-
- - -
-
- - -
-
- - -
-
- +
+
+
+
Templates
+
+ + + +
+ +
+ + + @RenderNodeLabel(node) + + + @RenderNodeContextMenu(node) + + + No templates yet. Use the buttons above to create a folder or template. + + +
+
+ +
+ @if (_selectedTemplate == null) + { +
+ Select a template on the left to view or edit. +
+ } + else + { + @RenderTemplateDetail() + }
- - @* Tabs: Attributes, Alarms, Scripts, Compositions *@ - - - @if (_activeTab == "attributes") - { - @RenderAttributesTab() - } - else if (_activeTab == "alarms") - { - @RenderAlarmsTab() - } - else if (_activeTab == "scripts") - { - @RenderScriptsTab() - } - else if (_activeTab == "compositions") - { - @RenderCompositionsTab() - } }
@@ -412,12 +287,193 @@ } } - private void BackToList() + private TreeView _tree = default!; + + private RenderFragment RenderTemplateDetail() => __builder => { - _selectedTemplate = null; - _validationResult = null; +
+
+

@_selectedTemplate!.Name

+ @if (_selectedTemplate.ParentTemplateId.HasValue) + { + inherits @(_templates.FirstOrDefault(t => t.Id == _selectedTemplate.ParentTemplateId)?.Name) + } +
+
+ +
+
+ + @* Validation results *@ + @if (_validationResult != null) + { +
+ @if (_validationResult.Errors.Count > 0) + { +
+ Validation Errors (@_validationResult.Errors.Count) +
    + @foreach (var err in _validationResult.Errors) + { +
  • [@err.Category] @err.Message @(err.EntityName != null ? $"({err.EntityName})" : "")
  • + } +
+
+ } + @if (_validationResult.Warnings.Count > 0) + { +
+ Warnings (@_validationResult.Warnings.Count) +
    + @foreach (var warn in _validationResult.Warnings) + { +
  • [@warn.Category] @warn.Message
  • + } +
+
+ } + @if (_validationResult.Errors.Count == 0 && _validationResult.Warnings.Count == 0) + { +
Validation passed with no errors or warnings.
+ } +
+ } + + @* Template info edit *@ +
+
Template Properties
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+ + @* Tabs: Attributes, Alarms, Scripts, Compositions *@ + + + @if (_activeTab == "attributes") + { + @RenderAttributesTab() + } + else if (_activeTab == "alarms") + { + @RenderAlarmsTab() + } + else if (_activeTab == "scripts") + { + @RenderScriptsTab() + } + else if (_activeTab == "compositions") + { + @RenderCompositionsTab() + } + }; + + private RenderFragment RenderNodeLabel(TmplNode node) => __builder => + { + switch (node.Kind) + { + case TmplNodeKind.Folder: + 📁 + @node.Label + @node.Children.Count + break; + case TmplNodeKind.Template: + @node.Label + if (node.Template?.ParentTemplateId is int pid) + { + inherits @(_templates.FirstOrDefault(t => t.Id == pid)?.Name) + } + + @node.Template!.Attributes.Count attr, + @node.Template.Alarms.Count alm, + @node.Template.Scripts.Count scr + + if (node.Template.Compositions.Count > 0) + { + @node.Template.Compositions.Count comp + } + break; + case TmplNodeKind.Composition: + @node.Label + → @(_templates.FirstOrDefault(t => t.Id == node.Composition!.ComposedTemplateId)?.Name ?? $"#{node.Composition!.ComposedTemplateId}") + break; + } + }; + + private async Task OnTreeNodeSelected(object? key) + { + if (key is not string s) return; + if (s.StartsWith("t:") && int.TryParse(s[2..], out var tid)) + { + await SelectTemplate(tid); + } + else if (s.StartsWith("c:") && int.TryParse(s[2..], out var cid)) + { + var comp = _templates.SelectMany(t => t.Compositions).FirstOrDefault(c => c.Id == cid); + if (comp != null) + { + // Reveal + select the composed template. + await _tree.RevealNode($"t:{comp.ComposedTemplateId}", select: true); + await SelectTemplate(comp.ComposedTemplateId); + } + } + // Folder selection: no-op (Section 4 design — folder click does not load detail). } + private RenderFragment RenderNodeContextMenu(TmplNode node) => __builder => { }; + + private void OpenNewFolderDialog(int? parentFolderId) { /* Task 17 */ } + private void OpenNewTemplateDialog(int? parentFolderId) { /* Task 17 */ } + private async Task DeleteTemplate(Template template) { var confirmed = await _confirmDialog.ShowAsync(