feat(ui): MES reportable checkbox on the template alarm dialog + MES column on the Alarms tab
Claude-Session: https://claude.ai/code/session_01Y5ef34iUUMjmwnMpL9xgmB
This commit is contained in:
+16
-2
@@ -41,6 +41,13 @@
|
||||
<label class="form-check-label" for="alarmLocked">Locked</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" @bind="_isMesReportable" id="alarmMesReportable" />
|
||||
<label class="form-check-label" for="alarmMesReportable">MES reportable</label>
|
||||
<div class="form-text">Returned by the MES alarm-status API as <code>IsFlaggedForMES</code>. Instances can override this unless the alarm is locked.</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (_error != null)
|
||||
{
|
||||
<div class="col-12"><div class="text-danger small" data-test="alarm-form-error">@_error</div></div>
|
||||
@@ -67,6 +74,9 @@
|
||||
[Parameter] public string? InitialTriggerConfig { get; set; }
|
||||
[Parameter] public bool InitialIsLocked { get; set; }
|
||||
|
||||
/// <summary>Seeds the "MES reportable" flag returned by the MES alarm-status API.</summary>
|
||||
[Parameter] public bool InitialIsMesReportable { get; set; }
|
||||
|
||||
/// <summary>Attribute choices offered by the structured trigger editor's picker.</summary>
|
||||
[Parameter] public IReadOnlyList<AlarmAttributeChoice> AvailableAttributes { get; set; }
|
||||
= Array.Empty<AlarmAttributeChoice>();
|
||||
@@ -82,6 +92,7 @@
|
||||
private AlarmTriggerType _triggerType;
|
||||
private string? _triggerConfig;
|
||||
private bool _isLocked;
|
||||
private bool _isMesReportable;
|
||||
private string? _error;
|
||||
private bool _busy;
|
||||
|
||||
@@ -92,6 +103,7 @@
|
||||
_triggerType = InitialTriggerType;
|
||||
_triggerConfig = InitialTriggerConfig;
|
||||
_isLocked = InitialIsLocked;
|
||||
_isMesReportable = InitialIsMesReportable;
|
||||
}
|
||||
|
||||
private async Task Submit()
|
||||
@@ -103,7 +115,7 @@
|
||||
_busy = true;
|
||||
try
|
||||
{
|
||||
var error = await OnSaveAsync(new AlarmDraft(_name, _triggerType, _priority, _triggerConfig, _isLocked));
|
||||
var error = await OnSaveAsync(new AlarmDraft(_name, _triggerType, _priority, _triggerConfig, _isLocked, _isMesReportable));
|
||||
if (error == null)
|
||||
{
|
||||
Context.Close(true);
|
||||
@@ -125,10 +137,12 @@
|
||||
/// <param name="Priority">Priority level.</param>
|
||||
/// <param name="TriggerConfig">Structured trigger configuration payload.</param>
|
||||
/// <param name="IsLocked">Whether instances may override the alarm.</param>
|
||||
/// <param name="IsMesReportable">Whether the MES alarm-status API reports this alarm.</param>
|
||||
public sealed record AlarmDraft(
|
||||
string Name,
|
||||
AlarmTriggerType TriggerType,
|
||||
int Priority,
|
||||
string? TriggerConfig,
|
||||
bool IsLocked);
|
||||
bool IsLocked,
|
||||
bool IsMesReportable);
|
||||
}
|
||||
|
||||
@@ -907,6 +907,7 @@
|
||||
<th>Trigger</th>
|
||||
<th>Priority</th>
|
||||
<th>Config</th>
|
||||
<th>MES</th>
|
||||
<th>Lock</th>
|
||||
<th style="width: 60px;">Actions</th>
|
||||
</tr>
|
||||
@@ -922,6 +923,16 @@
|
||||
config readable instead of merely hidden. *@
|
||||
<td class="small text-muted text-truncate" style="max-width: 200px;"
|
||||
title="@alarm.TriggerConfiguration">@(alarm.TriggerConfiguration ?? "—")</td>
|
||||
<td data-test="alarm-mes-badge">
|
||||
@if (alarm.IsMesReportable)
|
||||
{
|
||||
<span class="badge bg-success" aria-label="MES reportable">MES</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-light text-dark" aria-label="Not MES reportable">—</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (alarm.IsLocked)
|
||||
{
|
||||
@@ -1436,7 +1447,7 @@
|
||||
private Task OpenAddAlarmDialog()
|
||||
=> ShowAlarmDialogAsync(
|
||||
editId: null,
|
||||
new TemplateAlarmDialog.AlarmDraft(string.Empty, default, 500, null, false));
|
||||
new TemplateAlarmDialog.AlarmDraft(string.Empty, default, 500, null, false, false));
|
||||
|
||||
private Task OpenEditAlarmDialog(TemplateAlarm alarm)
|
||||
=> ShowAlarmDialogAsync(
|
||||
@@ -1446,7 +1457,8 @@
|
||||
alarm.TriggerType,
|
||||
alarm.PriorityLevel,
|
||||
alarm.TriggerConfiguration,
|
||||
alarm.IsLocked));
|
||||
alarm.IsLocked,
|
||||
alarm.IsMesReportable));
|
||||
|
||||
private async Task ShowAlarmDialogAsync(int? editId, TemplateAlarmDialog.AlarmDraft initial)
|
||||
{
|
||||
@@ -1461,6 +1473,7 @@
|
||||
InitialPriority="initial.Priority"
|
||||
InitialTriggerConfig="@initial.TriggerConfig"
|
||||
InitialIsLocked="initial.IsLocked"
|
||||
InitialIsMesReportable="initial.IsMesReportable"
|
||||
AvailableAttributes="choices"
|
||||
OnSaveAsync="d => SaveAlarmDraftAsync(editId, d)" />,
|
||||
size: "modal-dialog-scrollable");
|
||||
@@ -1521,6 +1534,7 @@
|
||||
PriorityLevel = draft.Priority,
|
||||
TriggerConfiguration = draft.TriggerConfig?.Trim(),
|
||||
IsLocked = draft.IsLocked,
|
||||
IsMesReportable = draft.IsMesReportable,
|
||||
Description = existing.Description,
|
||||
OnTriggerScriptId = existing.OnTriggerScriptId,
|
||||
};
|
||||
@@ -1536,7 +1550,8 @@
|
||||
TriggerType = draft.TriggerType,
|
||||
PriorityLevel = draft.Priority,
|
||||
TriggerConfiguration = draft.TriggerConfig?.Trim(),
|
||||
IsLocked = draft.IsLocked
|
||||
IsLocked = draft.IsLocked,
|
||||
IsMesReportable = draft.IsMesReportable
|
||||
};
|
||||
|
||||
var addResult = await TemplateService.AddAlarmAsync(_selectedTemplate.Id, alarm, user);
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Design;
|
||||
|
||||
/// <summary>
|
||||
/// The template alarm authoring surface exposes <c>TemplateAlarm.IsMesReportable</c>:
|
||||
/// an "MES reportable" checkbox in the dialog body and an MES column on the
|
||||
/// template's Alarms tab. <c>TemplateEdit</c> is a heavyweight page (~10 injected
|
||||
/// services with their own graphs), so — like the rest of the TemplateEdit coverage
|
||||
/// in this suite (see <c>TemplateNativeAlarmSourceEditorTests</c>) — these are
|
||||
/// structural assertions over the component source that pin the wiring so it cannot
|
||||
/// silently regress. The persistence behaviour itself is covered by the
|
||||
/// TemplateService / ManagementActor alarm tests.
|
||||
/// </summary>
|
||||
public class TemplateAlarmDialogMesReportableTests
|
||||
{
|
||||
private static string DesignComponentsRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
var dir = AppContext.BaseDirectory;
|
||||
for (var i = 0; i < 6 && dir is not null; i++)
|
||||
dir = Directory.GetParent(dir)?.FullName;
|
||||
return Path.Combine(dir!, "src", "ZB.MOM.WW.ScadaBridge.CentralUI",
|
||||
"Components", "Pages", "Design");
|
||||
}
|
||||
}
|
||||
|
||||
private static string AlarmDialogMarkup
|
||||
=> File.ReadAllText(Path.Combine(DesignComponentsRoot, "TemplateAlarmDialog.razor"));
|
||||
|
||||
private static string TemplateEditMarkup
|
||||
=> File.ReadAllText(Path.Combine(DesignComponentsRoot, "TemplateEdit.razor"));
|
||||
|
||||
[Fact]
|
||||
public void AlarmDialog_HasMesReportableCheckbox()
|
||||
{
|
||||
var markup = AlarmDialogMarkup;
|
||||
Assert.Contains("@bind=\"_isMesReportable\"", markup);
|
||||
Assert.Contains("id=\"alarmMesReportable\"", markup);
|
||||
Assert.Contains("MES reportable", markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlarmDialog_ThreadsMesReportableThroughParameterAndDraft()
|
||||
{
|
||||
var markup = AlarmDialogMarkup;
|
||||
Assert.Contains("InitialIsMesReportable", markup);
|
||||
Assert.Contains("_isMesReportable = InitialIsMesReportable", markup);
|
||||
Assert.Contains("bool IsMesReportable", markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlarmsTab_HasMesColumn()
|
||||
{
|
||||
var markup = TemplateEditMarkup;
|
||||
Assert.Contains("<th>MES</th>", markup);
|
||||
Assert.Contains("data-test=\"alarm-mes-badge\"", markup);
|
||||
Assert.Contains("alarm.IsMesReportable", markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlarmsTab_PassesMesReportableIntoAndOutOfTheDialog()
|
||||
{
|
||||
var markup = TemplateEditMarkup;
|
||||
Assert.Contains("InitialIsMesReportable=\"initial.IsMesReportable\"", markup);
|
||||
Assert.Contains("IsMesReportable = draft.IsMesReportable", markup);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user