Compare commits
3 Commits
redundancy
...
diff-viewe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8a38bc57b | ||
| cecb84fa5d | |||
| d1686ed82d |
@@ -0,0 +1,90 @@
|
|||||||
|
@using ZB.MOM.WW.OtOpcUa.Admin.Services
|
||||||
|
|
||||||
|
@* Per-section diff renderer — the base used by DiffViewer for every known TableName. Caps
|
||||||
|
output at RowCap rows so a pathological draft (e.g. 20k tags churned) can't freeze the
|
||||||
|
Blazor render; overflow banner tells operator how many rows were hidden. *@
|
||||||
|
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<div>
|
||||||
|
<strong>@Title</strong>
|
||||||
|
<small class="text-muted ms-2">@Description</small>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
@if (_added > 0) { <span class="badge bg-success me-1">+@_added</span> }
|
||||||
|
@if (_removed > 0) { <span class="badge bg-danger me-1">−@_removed</span> }
|
||||||
|
@if (_modified > 0) { <span class="badge bg-warning text-dark me-1">~@_modified</span> }
|
||||||
|
@if (_total == 0) { <span class="badge bg-secondary">no changes</span> }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (_total == 0)
|
||||||
|
{
|
||||||
|
<div class="card-body text-muted small">No changes in this section.</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@if (_total > RowCap)
|
||||||
|
{
|
||||||
|
<div class="alert alert-warning mb-0 small rounded-0">
|
||||||
|
Showing the first @RowCap of @_total rows — cap protects the browser from megabyte-class
|
||||||
|
diffs. Inspect the remainder via the SQL <code>sp_ComputeGenerationDiff</code> directly.
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
|
||||||
|
<table class="table table-sm table-hover mb-0">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr><th>LogicalId</th><th style="width: 120px;">Change</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var r in _visibleRows)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td><code>@r.LogicalId</code></td>
|
||||||
|
<td>
|
||||||
|
@switch (r.ChangeKind)
|
||||||
|
{
|
||||||
|
case "Added": <span class="badge bg-success">@r.ChangeKind</span> break;
|
||||||
|
case "Removed": <span class="badge bg-danger">@r.ChangeKind</span> break;
|
||||||
|
case "Modified": <span class="badge bg-warning text-dark">@r.ChangeKind</span> break;
|
||||||
|
default: <span class="badge bg-secondary">@r.ChangeKind</span> break;
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
/// <summary>Default row-cap per section — matches task #156's acceptance criterion.</summary>
|
||||||
|
public const int DefaultRowCap = 1000;
|
||||||
|
|
||||||
|
[Parameter, EditorRequired] public string Title { get; set; } = string.Empty;
|
||||||
|
[Parameter] public string Description { get; set; } = string.Empty;
|
||||||
|
[Parameter, EditorRequired] public IReadOnlyList<DiffRow> Rows { get; set; } = [];
|
||||||
|
[Parameter] public int RowCap { get; set; } = DefaultRowCap;
|
||||||
|
|
||||||
|
private int _total;
|
||||||
|
private int _added;
|
||||||
|
private int _removed;
|
||||||
|
private int _modified;
|
||||||
|
private List<DiffRow> _visibleRows = [];
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
_total = Rows.Count;
|
||||||
|
_added = 0; _removed = 0; _modified = 0;
|
||||||
|
foreach (var r in Rows)
|
||||||
|
{
|
||||||
|
switch (r.ChangeKind)
|
||||||
|
{
|
||||||
|
case "Added": _added++; break;
|
||||||
|
case "Removed": _removed++; break;
|
||||||
|
case "Modified": _modified++; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_visibleRows = _total > RowCap ? Rows.Take(RowCap).ToList() : Rows.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,36 +28,44 @@ else if (_rows.Count == 0)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<table class="table table-hover table-sm">
|
<p class="small text-muted mb-3">
|
||||||
<thead><tr><th>Table</th><th>LogicalId</th><th>ChangeKind</th></tr></thead>
|
@_rows.Count row@(_rows.Count == 1 ? "" : "s") across @_sectionsWithChanges of @Sections.Count sections.
|
||||||
<tbody>
|
Each section is capped at @DiffSection.DefaultRowCap rows to keep the browser responsive on pathological drafts.
|
||||||
@foreach (var r in _rows)
|
</p>
|
||||||
{
|
|
||||||
<tr>
|
@foreach (var sec in Sections)
|
||||||
<td>@r.TableName</td>
|
{
|
||||||
<td><code>@r.LogicalId</code></td>
|
<DiffSection Title="@sec.Title"
|
||||||
<td>
|
Description="@sec.Description"
|
||||||
@switch (r.ChangeKind)
|
Rows="@RowsFor(sec.TableName)"/>
|
||||||
{
|
}
|
||||||
case "Added": <span class="badge bg-success">@r.ChangeKind</span> break;
|
|
||||||
case "Removed": <span class="badge bg-danger">@r.ChangeKind</span> break;
|
|
||||||
case "Modified": <span class="badge bg-warning text-dark">@r.ChangeKind</span> break;
|
|
||||||
default: <span class="badge bg-secondary">@r.ChangeKind</span> break;
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] public string ClusterId { get; set; } = string.Empty;
|
[Parameter] public string ClusterId { get; set; } = string.Empty;
|
||||||
[Parameter] public long GenerationId { get; set; }
|
[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 List<DiffRow>? _rows;
|
||||||
private string _fromLabel = "(empty)";
|
private string _fromLabel = "(empty)";
|
||||||
private string? _error;
|
private string? _error;
|
||||||
|
private int _sectionsWithChanges;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
@@ -67,7 +75,13 @@ else
|
|||||||
var from = all.FirstOrDefault(g => g.Status == GenerationStatus.Published);
|
var from = all.FirstOrDefault(g => g.Status == GenerationStatus.Published);
|
||||||
_fromLabel = from is null ? "(empty)" : $"gen {from.GenerationId}";
|
_fromLabel = from is null ? "(empty)" : $"gen {from.GenerationId}";
|
||||||
_rows = await GenerationSvc.ComputeDiffAsync(from?.GenerationId ?? 0, GenerationId, CancellationToken.None);
|
_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; }
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user