88 lines
3.7 KiB
Plaintext
88 lines
3.7 KiB
Plaintext
@page "/clusters/{ClusterId}/draft/{GenerationId:long}/diff"
|
|
@using ZB.MOM.WW.OtOpcUa.Admin.Services
|
|
@using ZB.MOM.WW.OtOpcUa.Configuration.Entities
|
|
@using ZB.MOM.WW.OtOpcUa.Configuration.Enums
|
|
@inject GenerationService GenerationSvc
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<div>
|
|
<h1 class="mb-0">Draft diff</h1>
|
|
<small class="text-muted">
|
|
Cluster <code>@ClusterId</code> — from last published (@(_fromLabel)) → to draft @GenerationId
|
|
</small>
|
|
</div>
|
|
<a class="btn btn-outline-secondary" href="/clusters/@ClusterId/draft/@GenerationId">Back to editor</a>
|
|
</div>
|
|
|
|
@if (_rows is null)
|
|
{
|
|
<p>Computing diff…</p>
|
|
}
|
|
else if (_error is not null)
|
|
{
|
|
<div class="alert alert-danger">@_error</div>
|
|
}
|
|
else if (_rows.Count == 0)
|
|
{
|
|
<p class="text-muted">No differences — draft is structurally identical to the last published generation.</p>
|
|
}
|
|
else
|
|
{
|
|
<p class="small text-muted mb-3">
|
|
@_rows.Count row@(_rows.Count == 1 ? "" : "s") across @_sectionsWithChanges of @Sections.Count sections.
|
|
Each section is capped at @DiffSection.DefaultRowCap rows to keep the browser responsive on pathological drafts.
|
|
</p>
|
|
|
|
@foreach (var sec in Sections)
|
|
{
|
|
<DiffSection Title="@sec.Title"
|
|
Description="@sec.Description"
|
|
Rows="@RowsFor(sec.TableName)"/>
|
|
}
|
|
}
|
|
|
|
@code {
|
|
[Parameter] public string ClusterId { get; set; } = string.Empty;
|
|
[Parameter] public long GenerationId { get; set; }
|
|
|
|
/// <summary>
|
|
/// Ordered section definitions — each maps a <c>TableName</c> emitted by
|
|
/// <c>sp_ComputeGenerationDiff</c> to a human label + description. The proc currently
|
|
/// emits Namespace/DriverInstance/Equipment/Tag; UnsLine + NodeAcl entries render as
|
|
/// empty "no changes" cards until the proc is extended (tracked in tasks #196 + #156
|
|
/// follow-up). Six sections total matches the task #156 target.
|
|
/// </summary>
|
|
private static readonly IReadOnlyList<SectionDef> Sections = new[]
|
|
{
|
|
new SectionDef("Namespace", "Namespaces", "OPC UA namespace URIs + enablement"),
|
|
new SectionDef("DriverInstance", "Driver instances","Per-cluster driver configuration rows"),
|
|
new SectionDef("Equipment", "Equipment", "UNS level-5 rows + identification fields"),
|
|
new SectionDef("Tag", "Tags", "Per-device tag definitions + poll-group binding"),
|
|
new SectionDef("UnsLine", "UNS structure", "Site / Area / Line hierarchy (proc-extension pending)"),
|
|
new SectionDef("NodeAcl", "ACLs", "LDAP-group → node-scope permission grants (proc-extension pending)"),
|
|
};
|
|
|
|
private List<DiffRow>? _rows;
|
|
private string _fromLabel = "(empty)";
|
|
private string? _error;
|
|
private int _sectionsWithChanges;
|
|
|
|
protected override async Task OnParametersSetAsync()
|
|
{
|
|
try
|
|
{
|
|
var all = await GenerationSvc.ListRecentAsync(ClusterId, 50, CancellationToken.None);
|
|
var from = all.FirstOrDefault(g => g.Status == GenerationStatus.Published);
|
|
_fromLabel = from is null ? "(empty)" : $"gen {from.GenerationId}";
|
|
_rows = await GenerationSvc.ComputeDiffAsync(from?.GenerationId ?? 0, GenerationId, CancellationToken.None);
|
|
_sectionsWithChanges = Sections.Count(s => _rows.Any(r => r.TableName == s.TableName));
|
|
}
|
|
catch (Exception ex) { _error = ex.Message; }
|
|
}
|
|
|
|
private IReadOnlyList<DiffRow> RowsFor(string tableName) =>
|
|
_rows?.Where(r => r.TableName == tableName).ToList() ?? [];
|
|
|
|
private sealed record SectionDef(string TableName, string Title, string Description);
|
|
}
|