329 lines
11 KiB
Plaintext
329 lines
11 KiB
Plaintext
@using System
|
|
@using System.Collections.Generic
|
|
@using System.Linq
|
|
@inject LookupService LookupService
|
|
|
|
<section class="panel diagnostics-page tooling-surface">
|
|
<header class="diagnostics-page-header">
|
|
<div>
|
|
<h2 class="panel-title">Critical Cell Diagnostics</h2>
|
|
<p class="panel-copy">Engineering-only parser metadata, provenance, and payload inspection live here instead of inside the normal curation modal.</p>
|
|
</div>
|
|
</header>
|
|
|
|
@if (referenceData is null)
|
|
{
|
|
<p class="muted">Loading table list...</p>
|
|
}
|
|
else if (!referenceData.CriticalTables.Any())
|
|
{
|
|
<p class="muted">No critical tables are available yet.</p>
|
|
}
|
|
else
|
|
{
|
|
<div class="diagnostics-selector-grid">
|
|
<div class="field-shell">
|
|
<label for="diagnostics-table-select">Table</label>
|
|
<select
|
|
id="diagnostics-table-select"
|
|
class="input-shell"
|
|
value="@selectedTableSlug"
|
|
@onchange="HandleTableChanged"
|
|
disabled="@isBusy">
|
|
@foreach (var table in referenceData.CriticalTables)
|
|
{
|
|
<option value="@table.Key">@table.Label</option>
|
|
}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="field-shell">
|
|
<label for="diagnostics-roll-band-select">Roll Band</label>
|
|
<select
|
|
id="diagnostics-roll-band-select"
|
|
class="input-shell"
|
|
value="@selectedRollBand"
|
|
@onchange="HandleRollBandChanged"
|
|
disabled="@(isBusy || tableDetail is null || !tableDetail.RollBands.Any())">
|
|
@if (tableDetail is not null)
|
|
{
|
|
@foreach (var rollBand in tableDetail.RollBands)
|
|
{
|
|
<option value="@rollBand.Label">@rollBand.Label</option>
|
|
}
|
|
}
|
|
</select>
|
|
</div>
|
|
|
|
@if (tableDetail is { Groups.Count: > 0 })
|
|
{
|
|
<div class="field-shell">
|
|
<label for="diagnostics-group-select">Variant</label>
|
|
<select
|
|
id="diagnostics-group-select"
|
|
class="input-shell"
|
|
value="@selectedGroupKey"
|
|
@onchange="HandleGroupChanged"
|
|
disabled="@isBusy">
|
|
@foreach (var group in tableDetail.Groups)
|
|
{
|
|
<option value="@group.Key">@group.Label</option>
|
|
}
|
|
</select>
|
|
</div>
|
|
}
|
|
|
|
<div class="field-shell">
|
|
<label for="diagnostics-column-select">Severity</label>
|
|
<select
|
|
id="diagnostics-column-select"
|
|
class="input-shell"
|
|
value="@selectedColumnKey"
|
|
@onchange="HandleColumnChanged"
|
|
disabled="@(isBusy || tableDetail is null || !tableDetail.Columns.Any())">
|
|
@if (tableDetail is not null)
|
|
{
|
|
@foreach (var column in tableDetail.Columns)
|
|
{
|
|
<option value="@column.Key">@column.Label</option>
|
|
}
|
|
}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
@if (!string.IsNullOrWhiteSpace(detailError))
|
|
{
|
|
<p class="error-text">@detailError</p>
|
|
}
|
|
else if (tableDetail is null)
|
|
{
|
|
<p class="muted">The selected table could not be loaded.</p>
|
|
}
|
|
else if (!tableDetail.Cells.Any())
|
|
{
|
|
<p class="muted">The selected table has no filled cells to inspect.</p>
|
|
}
|
|
else if (selectedCell is null)
|
|
{
|
|
<div class="critical-editor-card nested">
|
|
<div class="critical-editor-card-header">
|
|
<div>
|
|
<strong>No Filled Cell At This Position</strong>
|
|
<p class="muted critical-editor-inline-copy">Pick another roll band, variant, or severity to inspect a stored result.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="diagnostics-selection-summary">
|
|
<strong>Inspecting</strong>
|
|
<span>@tableDetail.DisplayName</span>
|
|
<span>· Roll band <strong>@selectedCell.RollBand</strong></span>
|
|
<span>· Severity <strong>@selectedCell.ColumnLabel</strong></span>
|
|
@if (!string.IsNullOrWhiteSpace(selectedCell.GroupLabel))
|
|
{
|
|
<span>· Variant <strong>@selectedCell.GroupLabel</strong></span>
|
|
}
|
|
<span>· Result ID <strong>@selectedCell.ResultId</strong></span>
|
|
</div>
|
|
|
|
@if (isDiagnosticsLoading)
|
|
{
|
|
<p class="muted">Loading diagnostics...</p>
|
|
}
|
|
else if (!string.IsNullOrWhiteSpace(diagnosticsError))
|
|
{
|
|
<p class="error-text">@diagnosticsError</p>
|
|
}
|
|
else if (diagnosticsModel is not null)
|
|
{
|
|
<CriticalCellEngineeringDiagnostics Model="diagnosticsModel" />
|
|
}
|
|
}
|
|
}
|
|
</section>
|
|
|
|
@code {
|
|
private LookupReferenceData? referenceData;
|
|
private CriticalTableDetail? tableDetail;
|
|
private CriticalTableCellDetail? selectedCell;
|
|
private CriticalCellEditorModel? diagnosticsModel;
|
|
private string selectedTableSlug = string.Empty;
|
|
private string selectedRollBand = string.Empty;
|
|
private string selectedColumnKey = string.Empty;
|
|
private string? selectedGroupKey;
|
|
private bool isDetailLoading;
|
|
private bool isDiagnosticsLoading;
|
|
private string? detailError;
|
|
private string? diagnosticsError;
|
|
|
|
private bool isBusy => isDetailLoading || isDiagnosticsLoading;
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
referenceData = await LookupService.GetReferenceDataAsync();
|
|
selectedTableSlug = referenceData.CriticalTables.FirstOrDefault()?.Key ?? string.Empty;
|
|
await LoadTableDetailAsync();
|
|
}
|
|
|
|
private async Task HandleTableChanged(ChangeEventArgs args)
|
|
{
|
|
selectedTableSlug = args.Value?.ToString() ?? string.Empty;
|
|
await LoadTableDetailAsync();
|
|
}
|
|
|
|
private async Task HandleRollBandChanged(ChangeEventArgs args)
|
|
{
|
|
selectedRollBand = args.Value?.ToString() ?? string.Empty;
|
|
ResolveSelectedCell();
|
|
await LoadSelectedCellDiagnosticsAsync();
|
|
}
|
|
|
|
private async Task HandleGroupChanged(ChangeEventArgs args)
|
|
{
|
|
selectedGroupKey = NormalizeOptionalText(args.Value?.ToString());
|
|
ResolveSelectedCell();
|
|
await LoadSelectedCellDiagnosticsAsync();
|
|
}
|
|
|
|
private async Task HandleColumnChanged(ChangeEventArgs args)
|
|
{
|
|
selectedColumnKey = args.Value?.ToString() ?? string.Empty;
|
|
ResolveSelectedCell();
|
|
await LoadSelectedCellDiagnosticsAsync();
|
|
}
|
|
|
|
private async Task LoadTableDetailAsync()
|
|
{
|
|
if (string.IsNullOrWhiteSpace(selectedTableSlug))
|
|
{
|
|
tableDetail = null;
|
|
selectedCell = null;
|
|
diagnosticsModel = null;
|
|
return;
|
|
}
|
|
|
|
isDetailLoading = true;
|
|
detailError = null;
|
|
diagnosticsError = null;
|
|
diagnosticsModel = null;
|
|
selectedCell = null;
|
|
|
|
try
|
|
{
|
|
tableDetail = await LookupService.GetCriticalTableAsync(selectedTableSlug);
|
|
if (tableDetail is null)
|
|
{
|
|
detailError = "The selected table could not be loaded.";
|
|
return;
|
|
}
|
|
|
|
SetDefaultSelection(tableDetail);
|
|
ResolveSelectedCell();
|
|
await LoadSelectedCellDiagnosticsAsync();
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
detailError = exception.Message;
|
|
tableDetail = null;
|
|
selectedCell = null;
|
|
diagnosticsModel = null;
|
|
}
|
|
finally
|
|
{
|
|
isDetailLoading = false;
|
|
}
|
|
}
|
|
|
|
private void SetDefaultSelection(CriticalTableDetail detail)
|
|
{
|
|
if (!detail.Cells.Any())
|
|
{
|
|
selectedRollBand = detail.RollBands.FirstOrDefault()?.Label ?? string.Empty;
|
|
selectedColumnKey = detail.Columns.FirstOrDefault()?.Key ?? string.Empty;
|
|
selectedGroupKey = detail.Groups.FirstOrDefault()?.Key;
|
|
return;
|
|
}
|
|
|
|
var rollOrder = detail.RollBands
|
|
.Select((rollBand, index) => new { rollBand.Label, index })
|
|
.ToDictionary(item => item.Label, item => item.index, StringComparer.Ordinal);
|
|
var columnOrder = detail.Columns
|
|
.Select((column, index) => new { column.Key, index })
|
|
.ToDictionary(item => item.Key, item => item.index, StringComparer.Ordinal);
|
|
var groupOrder = detail.Groups
|
|
.Select((group, index) => new { group.Key, index })
|
|
.ToDictionary(item => item.Key, item => item.index, StringComparer.Ordinal);
|
|
|
|
var firstCell = detail.Cells
|
|
.OrderBy(cell => rollOrder.GetValueOrDefault(cell.RollBand, int.MaxValue))
|
|
.ThenBy(cell =>
|
|
{
|
|
if (cell.GroupKey is null)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return groupOrder.GetValueOrDefault(cell.GroupKey, int.MaxValue);
|
|
})
|
|
.ThenBy(cell => columnOrder.GetValueOrDefault(cell.ColumnKey, int.MaxValue))
|
|
.First();
|
|
|
|
selectedRollBand = firstCell.RollBand;
|
|
selectedColumnKey = firstCell.ColumnKey;
|
|
selectedGroupKey = firstCell.GroupKey;
|
|
}
|
|
|
|
private void ResolveSelectedCell()
|
|
{
|
|
if (tableDetail is null)
|
|
{
|
|
selectedCell = null;
|
|
return;
|
|
}
|
|
|
|
selectedCell = tableDetail.Cells.FirstOrDefault(cell =>
|
|
string.Equals(cell.RollBand, selectedRollBand, StringComparison.Ordinal) &&
|
|
string.Equals(cell.ColumnKey, selectedColumnKey, StringComparison.Ordinal) &&
|
|
string.Equals(cell.GroupKey ?? string.Empty, selectedGroupKey ?? string.Empty, StringComparison.Ordinal));
|
|
}
|
|
|
|
private async Task LoadSelectedCellDiagnosticsAsync()
|
|
{
|
|
diagnosticsError = null;
|
|
diagnosticsModel = null;
|
|
|
|
if (selectedCell is null || string.IsNullOrWhiteSpace(selectedTableSlug))
|
|
{
|
|
return;
|
|
}
|
|
|
|
isDiagnosticsLoading = true;
|
|
|
|
try
|
|
{
|
|
var response = await LookupService.GetCriticalCellEditorAsync(selectedTableSlug, selectedCell.ResultId);
|
|
if (response is null)
|
|
{
|
|
diagnosticsError = "The selected cell could not be loaded.";
|
|
return;
|
|
}
|
|
|
|
diagnosticsModel = CriticalCellEditorModel.FromResponse(response);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
diagnosticsError = exception.Message;
|
|
}
|
|
finally
|
|
{
|
|
isDiagnosticsLoading = false;
|
|
}
|
|
}
|
|
|
|
private static string? NormalizeOptionalText(string? value) =>
|
|
string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
|
}
|