fix(adminui): confirm equipment-page and script deletes (R04 H1)

The equipment page's Remove reference / Delete virtual tag / Delete alarm row
buttons and the ScriptEdit Delete ran on the first click. They now go through
RawConfirmDialog, the same confirm /raw uses. The ScriptEdit change is limited
to the delete button and its dialog.

Refs #745
Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
Joseph Doherty
2026-10-22 17:53:06 -04:00
parent e16146fa0c
commit a4cdedc31c
3 changed files with 98 additions and 4 deletions
@@ -11,6 +11,7 @@
@using System.Text
@using ZB.MOM.WW.OtOpcUa.Configuration
@using ZB.MOM.WW.OtOpcUa.Configuration.Entities
@using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Raw
@inject IDbContextFactory<OtOpcUaConfigDbContext> DbFactory
@inject NavigationManager Nav
@@ -69,9 +70,14 @@ else
<button type="submit" class="btn btn-primary" disabled="@_busy">@(IsNew ? "Create" : "Save changes")</button>
<a href="/scripts" class="btn btn-outline-secondary">Cancel</a>
</div>
@if (!IsNew) { <button type="button" class="btn btn-outline-danger ms-auto" @onclick="DeleteAsync" disabled="@_busy">Delete</button> }
@if (!IsNew) { <button type="button" class="btn btn-outline-danger ms-auto" @onclick="() => _confirmDeleteVisible = true" disabled="@_busy">Delete</button> }
</div>
</EditForm>
@* R04 H1: the delete used to run on the first click. *@
<RawConfirmDialog @bind-Visible="_confirmDeleteVisible" Title="Delete script"
Message="@($"Delete the script '{_existing?.Name ?? _form.Name}'? This cannot be undone.")"
OnConfirm="DeleteAsync" />
}
</PageErrorBoundary>
@@ -84,6 +90,7 @@ else
private bool _loaded;
private bool _busy;
private string? _error;
private bool _confirmDeleteVisible;
protected override async Task OnInitializedAsync()
{
@@ -11,6 +11,7 @@
@using ZB.MOM.WW.OtOpcUa.Configuration.Validation
@using ZB.MOM.WW.OtOpcUa.AdminUI.Uns
@using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Uns
@using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Raw
@inject IUnsTreeService Svc
@inject NavigationManager Nav
@@ -187,7 +188,7 @@ else
<td class="text-end text-nowrap">
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-secondary btn-sm" @onclick="() => SaveOverride(r)">Save name</button>
<button type="button" class="btn btn-outline-danger btn-sm" @onclick="() => RemoveReference(r)">Remove</button>
<button type="button" class="btn btn-outline-danger btn-sm" @onclick="() => ConfirmRemoveReference(r)">Remove</button>
</div>
</td>
</tr>
@@ -240,7 +241,7 @@ else
<td class="text-end">
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-secondary btn-sm" @onclick="() => OpenEditVirtualTag(v.VirtualTagId)">Edit</button>
<button type="button" class="btn btn-outline-danger btn-sm" @onclick="() => DeleteVirtualTag(v.VirtualTagId)">Delete</button>
<button type="button" class="btn btn-outline-danger btn-sm" @onclick="() => ConfirmDeleteVirtualTag(v.VirtualTagId, v.Name)">Delete</button>
</div>
</td>
</tr>
@@ -295,7 +296,7 @@ else
<td class="text-end">
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-secondary btn-sm" @onclick="() => OpenEditAlarm(a.ScriptedAlarmId)">Edit</button>
<button type="button" class="btn btn-outline-danger btn-sm" @onclick="() => DeleteAlarm(a.ScriptedAlarmId)">Delete</button>
<button type="button" class="btn btn-outline-danger btn-sm" @onclick="() => ConfirmDeleteAlarm(a.ScriptedAlarmId, a.Name)">Delete</button>
</div>
</td>
</tr>
@@ -310,6 +311,11 @@ else
<ScriptedAlarmModal Visible="_alarmModalVisible" IsNew="_alarmModalIsNew" EquipmentId="@EquipmentId"
Existing="_alarmModalExisting" Scripts="_alarmScriptOptions"
OnSaved="OnAlarmSavedAsync" OnCancel="@(() => { _alarmModalVisible = false; })" />
@* One confirm for every destructive row action on this page (R04 H1) — the same RawConfirmDialog
/raw routes its deletes through. A mis-click used to delete immediately. *@
<RawConfirmDialog @bind-Visible="_confirmVisible" Title="@_confirmTitle" Message="@_confirmMessage"
ConfirmLabel="@_confirmLabel" OnConfirm="RunConfirmedAsync" />
}
}
</PageErrorBoundary>
@@ -403,6 +409,47 @@ else
else { _refError = res.Error; }
}
// --- Delete confirmation (R04 H1) ---
private bool _confirmVisible;
private string _confirmTitle = "";
private string _confirmMessage = "";
private string _confirmLabel = "Delete";
private Func<Task>? _confirmAction;
private void AskConfirm(string title, string message, string label, Func<Task> action)
{
_confirmTitle = title;
_confirmMessage = message;
_confirmLabel = label;
_confirmAction = action;
_confirmVisible = true;
}
private async Task RunConfirmedAsync()
{
var action = _confirmAction;
_confirmAction = null;
if (action is not null) await action();
}
private void ConfirmRemoveReference(EquipmentReferenceRow r) => AskConfirm(
"Remove reference",
$"Remove the reference '{r.EffectiveName}'? After the next deploy its UNS node is gone, and any client subscribed to it loses it. The raw tag is not affected.",
"Remove",
() => RemoveReference(r));
private void ConfirmDeleteVirtualTag(string vtagId, string name) => AskConfirm(
"Delete virtual tag",
$"Delete the virtual tag '{name}'? After the next deploy its OPC UA node is gone, and any client subscribed to it loses it.",
"Delete",
() => DeleteVirtualTag(vtagId));
private void ConfirmDeleteAlarm(string scriptedAlarmId, string name) => AskConfirm(
"Delete scripted alarm",
$"Delete the scripted alarm '{name}'? After the next deploy its condition is gone, and any client subscribed to it loses it.",
"Delete",
() => DeleteAlarm(scriptedAlarmId));
private async Task RemoveReference(EquipmentReferenceRow r)
{
_refError = null;