diff --git a/src/ScadaLink.CentralUI/Components/Pages/Design/TemplateEdit.razor b/src/ScadaLink.CentralUI/Components/Pages/Design/TemplateEdit.razor index 36af7a1..8ae5ede 100644 --- a/src/ScadaLink.CentralUI/Components/Pages/Design/TemplateEdit.razor +++ b/src/ScadaLink.CentralUI/Components/Pages/Design/TemplateEdit.razor @@ -94,7 +94,22 @@ = Array.Empty(); private IReadOnlyList _editorChildren = Array.Empty(); - private ScadaLink.CentralUI.ScriptAnalysis.CompositionContext? _editorParent; + + /// + /// One entry per template that composes this one (in the design-time + /// graph). Populated by ; the user + /// picks one via when multiple exist. + /// + private List _editorParents + = new(); + + /// Index into ; -1 when none. + private int _selectedParentIndex = -1; + + private ScadaLink.CentralUI.ScriptAnalysis.CompositionContext? ActiveEditorParent => + _selectedParentIndex >= 0 && _selectedParentIndex < _editorParents.Count + ? _editorParents[_selectedParentIndex] + : null; private bool _showCompForm; private int _compComposedTemplateId; @@ -129,12 +144,14 @@ _scripts = (await TemplateEngineRepository.GetScriptsByTemplateIdAsync(Id)).ToList(); _compositions = (await TemplateEngineRepository.GetCompositionsByTemplateIdAsync(Id)).ToList(); - // Editor metadata: child compositions + parent (if exactly one). - // Powers Attributes["X"] / Children["Y"].Attributes["Z"] / - // Parent.Attributes["W"] completion + SCADA006 / SCADA007 diagnostics - // in the Monaco editor. + // Editor metadata: child compositions + every parent that + // composes this template. Powers Attributes["X"] / + // Children["Y"].Attributes["Z"] / Parent.Attributes["W"] + // completion + SCADA006 / SCADA007 diagnostics in the Monaco + // editor. _editorChildren = await BuildChildContextsAsync(_compositions); - _editorParent = await TryGetParentContextAsync(Id); + _editorParents = await BuildParentContextsAsync(Id); + _selectedParentIndex = _editorParents.Count > 0 ? 0 : -1; _validationResult = null; } @@ -667,6 +684,21 @@
+ @if (_editorParents.Count > 1) + { +
+ Parent context for editor: + + (this template is composed by @_editorParents.Count parents; pick one for Parent.* assistance) +
+ }
@@ -999,13 +1031,12 @@ return result; } - private async Task TryGetParentContextAsync(int templateId) + private async Task> BuildParentContextsAsync(int templateId) { - var all = await TemplateEngineRepository.GetAllTemplatesAsync(); - var parents = all.Where(t => t.Compositions.Any(c => c.ComposedTemplateId == templateId)).ToList(); - if (parents.Count != 1) return null; // ambiguous or root — suppress Parent assistance - var p = await TemplateEngineRepository.GetTemplateWithChildrenAsync(parents[0].Id); - return p == null ? null : BuildCompositionContext(p.Name, p); + var parents = await TemplateEngineRepository.GetTemplatesComposingAsync(templateId); + return parents + .Select(p => BuildCompositionContext(p.Name, p)) + .ToList(); } private static ScadaLink.CentralUI.ScriptAnalysis.CompositionContext BuildCompositionContext( @@ -1022,6 +1053,12 @@ return new ScadaLink.CentralUI.ScriptAnalysis.CompositionContext(label, attrs, scripts); } + private void OnParentContextChanged(ChangeEventArgs e) + { + if (int.TryParse(e.Value?.ToString(), out var idx) && idx >= 0 && idx < _editorParents.Count) + _selectedParentIndex = idx; + } + private static string MapDataType(ScadaLink.Commons.Types.Enums.DataType dt) => dt switch { ScadaLink.Commons.Types.Enums.DataType.Boolean => "Boolean", diff --git a/src/ScadaLink.Commons/Interfaces/Repositories/ITemplateEngineRepository.cs b/src/ScadaLink.Commons/Interfaces/Repositories/ITemplateEngineRepository.cs index d53b75c..397aa33 100644 --- a/src/ScadaLink.Commons/Interfaces/Repositories/ITemplateEngineRepository.cs +++ b/src/ScadaLink.Commons/Interfaces/Repositories/ITemplateEngineRepository.cs @@ -10,6 +10,13 @@ public interface ITemplateEngineRepository Task GetTemplateByIdAsync(int id, CancellationToken cancellationToken = default); Task GetTemplateWithChildrenAsync(int id, CancellationToken cancellationToken = default); Task> GetAllTemplatesAsync(CancellationToken cancellationToken = default); + /// + /// Returns every template that contains a composition referencing + /// . Each result is eager-loaded with + /// its Attributes / Scripts / Compositions so the caller can build a + /// CompositionContext without a follow-up round-trip per parent. + /// + Task> GetTemplatesComposingAsync(int composedTemplateId, CancellationToken cancellationToken = default); Task AddTemplateAsync(Template template, CancellationToken cancellationToken = default); Task UpdateTemplateAsync(Template template, CancellationToken cancellationToken = default); Task DeleteTemplateAsync(int id, CancellationToken cancellationToken = default); diff --git a/src/ScadaLink.ConfigurationDatabase/Repositories/TemplateEngineRepository.cs b/src/ScadaLink.ConfigurationDatabase/Repositories/TemplateEngineRepository.cs index 910d4d2..bc9bfa2 100644 --- a/src/ScadaLink.ConfigurationDatabase/Repositories/TemplateEngineRepository.cs +++ b/src/ScadaLink.ConfigurationDatabase/Repositories/TemplateEngineRepository.cs @@ -50,6 +50,16 @@ public class TemplateEngineRepository : ITemplateEngineRepository .ToListAsync(cancellationToken); } + public async Task> GetTemplatesComposingAsync(int composedTemplateId, CancellationToken cancellationToken = default) + { + return await _context.Templates + .Where(t => t.Compositions.Any(c => c.ComposedTemplateId == composedTemplateId)) + .Include(t => t.Attributes) + .Include(t => t.Scripts) + .Include(t => t.Compositions) + .ToListAsync(cancellationToken); + } + public async Task AddTemplateAsync(Template template, CancellationToken cancellationToken = default) { await _context.Templates.AddAsync(template, cancellationToken);