Files
ScadaBridge/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/MoveDataConnectionDialog.razor
T
Joseph Doherty 9e243493fb ui: Central UI density/consistency sweep + Theme 0.4.1
Applies the family-wide admin-UI cleanup playbook to the Central UI so the
Blazor surfaces stop diverging from the shared kit: buttons are grouped rather
than individually sized, long cell values are contained instead of widening
tables, and hard-coded colours give way to theme tokens.

The headline fix is that MainLayout passed Accent="#2f5fd0" to ThemeShell,
which the kit emits as an inline style on the shell root. Being a descendant of
<html>, it beat the [data-bs-theme="dark"] override for the entire app, so the
dark accent had never rendered. Declaring --accent in site.css :root instead
lets both schemes resolve; light is unchanged because the value already matched
the kit's light default.

Theme pins to 0.4.1, which upstreams the local .btn sizing block verbatim, so
that block is deleted here rather than duplicated. Verified byte-identical
before removal; the repo now declares no --bs-btn-* anywhere.

NOT purely cosmetic, contrary to the sweep's stated scope: four detail-modal
surfaces (NotificationReport, ConfigurationAuditLog, ParkedMessages,
SiteCallsReport) were additionally refactored from holding the selected record
to holding its id and re-resolving from the current page each render, with the
resolve doubling as the visibility gate. A background refresh that drops the
row now closes the modal instead of showing a stale snapshot. This is a
behaviour change and is called out rather than buried: a full-suite run turned
up one intermittent CentralUI failure, CloseButton_DismissesModal, whose stack
(GetRequiredEventBindingEntry during DispatchEventAsync) indicates the handler
was disposed between render and click — a window the previous field-held record
made structurally impossible. Treat the modal lifecycle here as unreviewed.

Build 0/0; suite green apart from that one intermittent failure.
2026-08-11 05:50:12 -04:00

90 lines
3.1 KiB
Plaintext

@using ZB.MOM.WW.ScadaBridge.CentralUI.Services
@inject IDataConnectionMoveService MoveService
@*
M9-T24b / T33b: body component for the "Move data connection" dialog hosted by
IDialogService.ShowAsync. Renders ONLY the site picker + action buttons inside
the host's .modal-body; the host owns the backdrop, header, and focus trap.
The body still injects IDataConnectionMoveService and dispatches the
MoveDataConnectionCommand through the guard-running ManagementActor path (NOT a
direct repository write) so the server's Designer gate and every move guard run.
A guard error is shown inline and the dialog STAYS OPEN; success closes the
dialog with Close(true) so the parent reloads the tree. Cancel resolves to false.
*@
@if (SiteOptions.Any())
{
<select class="form-select form-select-sm" @bind="_targetSiteId">
@foreach (var opt in SiteOptions)
{
<option value="@opt.Id">@opt.Label</option>
}
</select>
}
else
{
<div class="text-muted small">No other site is available to move this connection to.</div>
}
@if (!string.IsNullOrEmpty(_error))
{
<div class="text-danger small mt-2" data-test="move-connection-error">@_error</div>
}
<div class="modal-footer px-0 pb-0 mt-3">
<div class="btn-group btn-group-sm" role="group">
<button class="btn btn-outline-secondary" @onclick="() => Context.Cancel()" disabled="@_busy">Cancel</button>
<button class="btn btn-primary" @onclick="Submit"
disabled="@(_busy || !SiteOptions.Any())">Move</button>
</div>
</div>
@code {
/// <summary>Host-supplied context: Close(true) on a successful move, Cancel on dismiss.</summary>
[Parameter] public DialogContext<bool> Context { get; set; } = default!;
[Parameter] public int ConnectionId { get; set; }
[Parameter] public string ConnectionName { get; set; } = string.Empty;
[Parameter] public IEnumerable<(int Id, string Label)> SiteOptions { get; set; } = Array.Empty<(int, string)>();
private int? _targetSiteId;
private string? _error;
private bool _busy;
protected override void OnInitialized()
{
// Default the picker to the first candidate site.
_targetSiteId = SiteOptions.Select(o => (int?)o.Id).FirstOrDefault();
}
private async Task Submit()
{
if (_targetSiteId is not int target || _busy) return;
_busy = true;
_error = null;
try
{
var result = await MoveService.MoveAsync(ConnectionId, target);
if (result.Success)
{
// Success closes the dialog; the parent's post-ShowAsync block toasts
// and reloads the tree.
Context.Close(true);
}
else
{
// Surface the server guard error inline; keep the dialog open.
_error = result.Error ?? "Move failed.";
}
}
catch (Exception ex)
{
_error = $"Move failed: {ex.Message}";
}
finally
{
_busy = false;
}
}
}