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:
@@ -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;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;
|
||||
|
||||
/// <summary>
|
||||
/// R04 H1 source pin (no bUnit): every destructive button on the equipment page and the script editor
|
||||
/// opens a confirm dialog instead of deleting on the first click. The live check (each delete confirms)
|
||||
/// is in the Wave 4 docker-dev pass.
|
||||
/// </summary>
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class DeleteConfirmationWiringTests
|
||||
{
|
||||
[Fact]
|
||||
public void Equipment_page_routes_every_destructive_row_action_through_the_confirm()
|
||||
{
|
||||
var src = AdminUiSource.Read("Components", "Pages", "Uns", "EquipmentPage.razor");
|
||||
|
||||
src.ShouldContain("<RawConfirmDialog @bind-Visible=\"_confirmVisible\"");
|
||||
src.ShouldContain("@onclick=\"() => ConfirmRemoveReference(r)\"");
|
||||
src.ShouldContain("@onclick=\"() => ConfirmDeleteVirtualTag(v.VirtualTagId, v.Name)\"");
|
||||
src.ShouldContain("@onclick=\"() => ConfirmDeleteAlarm(a.ScriptedAlarmId, a.Name)\"");
|
||||
|
||||
// No danger button may call a delete handler directly.
|
||||
Regex.Matches(src, @"btn-outline-danger[^>]*@onclick=""\(\) => (Delete|Remove)\w*\(").Count.ShouldBe(0);
|
||||
// Positive control: the page still has its three danger buttons.
|
||||
Regex.Matches(src, "btn-outline-danger").Count.ShouldBe(3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Script_editor_delete_confirms()
|
||||
{
|
||||
var src = AdminUiSource.Read("Components", "Pages", "ScriptEdit.razor");
|
||||
|
||||
src.ShouldContain("@onclick=\"() => _confirmDeleteVisible = true\"");
|
||||
src.ShouldContain("OnConfirm=\"DeleteAsync\"");
|
||||
src.ShouldNotContain("@onclick=\"DeleteAsync\"");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user