diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/DebugView.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/DebugView.razor
index bd57adcd..7238786c 100644
--- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/DebugView.razor
+++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/DebugView.razor
@@ -209,33 +209,7 @@
{
๐ฌ
}
- @node.Alarm.State
- @FormatKind(node.Alarm.Kind)
- @if (node.Alarm.Kind != AlarmKind.Computed)
- {
- @if (node.Alarm.Condition.Active && !node.Alarm.Condition.Acknowledged)
- {
- Unacked
- }
- @if (node.Alarm.Condition.Shelve != AlarmShelveState.Unshelved)
- {
- Shelved
- }
- @if (node.Alarm.Condition.Suppressed)
- {
- Suppressed
- }
- }
- sev @node.Alarm.Condition.Severity
- @if (node.Alarm.Level != AlarmLevel.None)
- {
- @FormatLevel(node.Alarm.Level)
- }
+
}
else if (node.IsNativeBinding)
{
@@ -577,6 +551,10 @@
_ => "bg-secondary"
};
+ ///
+ /// State badge class shared by the alarm leaf (via AlarmStateBadges)
+ /// and the native/composition branch summary badges below (worst-state roll-up).
+ ///
private static string GetAlarmStateBadge(AlarmState state) => state switch
{
AlarmState.Active => "bg-danger",
@@ -584,32 +562,6 @@
_ => "bg-secondary"
};
- ///
- /// Severity-tinted badge class for HiLo alarm levels. The critical bands
- /// (HighHigh / LowLow) get the danger class; warning bands get amber.
- ///
- private static string GetAlarmLevelBadge(AlarmLevel level) => level switch
- {
- AlarmLevel.HighHigh or AlarmLevel.LowLow => "bg-danger",
- AlarmLevel.High or AlarmLevel.Low => "bg-warning text-dark",
- _ => "bg-secondary"
- };
-
- /// Badge class distinguishing computed (neutral) from native (info) alarms.
- private static string GetKindBadge(AlarmKind kind) => kind switch
- {
- AlarmKind.Computed => "bg-secondary",
- _ => "bg-info text-dark"
- };
-
- /// Short display label for the alarm kind.
- private static string FormatKind(AlarmKind kind) => kind switch
- {
- AlarmKind.NativeOpcUa => "OPC UA",
- AlarmKind.NativeMxAccess => "MxAccess",
- _ => "Computed"
- };
-
///
/// Builds the row tooltip from the alarm's operator message plus native
/// metadata (type, category, operator, raise time, current/limit value).
@@ -629,15 +581,6 @@
return parts.Count == 0 ? null : string.Join(" ยท ", parts);
}
- private static string FormatLevel(AlarmLevel level) => level switch
- {
- AlarmLevel.HighHigh => "HiHi",
- AlarmLevel.High => "Hi",
- AlarmLevel.Low => "Lo",
- AlarmLevel.LowLow => "LoLo",
- _ => "โ"
- };
-
///
/// Runs on the render thread, guarded against the
/// component being disposed mid-flight (CentralUI-009): InvokeAsync
diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/AlarmStateBadges.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/AlarmStateBadges.razor
new file mode 100644
index 00000000..66e9738b
--- /dev/null
+++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/AlarmStateBadges.razor
@@ -0,0 +1,80 @@
+@using ZB.MOM.WW.ScadaBridge.Commons.Messages.Streaming
+@using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums
+
+@* โโ Alarm leaf badge cluster โโ *@
+@* State / kind / native sub-state / severity / HiLo level badges for one alarm. *@
+@* Lifted verbatim from DebugView so the Alarm Summary page renders identically. *@
+@Alarm.State
+@FormatKind(Alarm.Kind)
+@if (Alarm.Kind != AlarmKind.Computed)
+{
+ @if (Alarm.Condition.Active && !Alarm.Condition.Acknowledged)
+ {
+ Unacked
+ }
+ @if (Alarm.Condition.Shelve != AlarmShelveState.Unshelved)
+ {
+ Shelved
+ }
+ @if (Alarm.Condition.Suppressed)
+ {
+ Suppressed
+ }
+}
+sev @Alarm.Condition.Severity
+@if (Alarm.Level != AlarmLevel.None)
+{
+ @FormatLevel(Alarm.Level)
+}
+
+@code {
+ /// The alarm condition whose badges this component renders.
+ [Parameter] public AlarmStateChanged Alarm { get; set; } = default!;
+
+ private static string GetAlarmStateBadge(AlarmState state) => state switch
+ {
+ AlarmState.Active => "bg-danger",
+ AlarmState.Normal => "bg-success",
+ _ => "bg-secondary"
+ };
+
+ ///
+ /// Severity-tinted badge class for HiLo alarm levels. The critical bands
+ /// (HighHigh / LowLow) get the danger class; warning bands get amber.
+ ///
+ private static string GetAlarmLevelBadge(AlarmLevel level) => level switch
+ {
+ AlarmLevel.HighHigh or AlarmLevel.LowLow => "bg-danger",
+ AlarmLevel.High or AlarmLevel.Low => "bg-warning text-dark",
+ _ => "bg-secondary"
+ };
+
+ /// Badge class distinguishing computed (neutral) from native (info) alarms.
+ private static string GetKindBadge(AlarmKind kind) => kind switch
+ {
+ AlarmKind.Computed => "bg-secondary",
+ _ => "bg-info text-dark"
+ };
+
+ /// Short display label for the alarm kind.
+ private static string FormatKind(AlarmKind kind) => kind switch
+ {
+ AlarmKind.NativeOpcUa => "OPC UA",
+ AlarmKind.NativeMxAccess => "MxAccess",
+ _ => "Computed"
+ };
+
+ private static string FormatLevel(AlarmLevel level) => level switch
+ {
+ AlarmLevel.HighHigh => "HiHi",
+ AlarmLevel.High => "Hi",
+ AlarmLevel.Low => "Lo",
+ AlarmLevel.LowLow => "LoLo",
+ _ => "โ"
+ };
+}
diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Components/AlarmStateBadgesTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Components/AlarmStateBadgesTests.cs
new file mode 100644
index 00000000..35957146
--- /dev/null
+++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Components/AlarmStateBadgesTests.cs
@@ -0,0 +1,58 @@
+using Bunit;
+using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
+using ZB.MOM.WW.ScadaBridge.Commons.Messages.Streaming;
+using ZB.MOM.WW.ScadaBridge.Commons.Types.Alarms;
+using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
+
+namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components;
+
+///
+/// Covers the AlarmStateBadges shared component lifted out of
+/// DebugView (M7-T13 prep). The badge cluster must render identically
+/// wherever it is hosted, so these tests pin the rendered markup: state badge
+/// class, kind label, native sub-state badges, severity, and HiLo level.
+///
+public class AlarmStateBadgesTests : BunitContext
+{
+ private static AlarmStateChanged ActiveNativeHighUnacked() => new(
+ InstanceUniqueName: "Tank01",
+ AlarmName: "Tank01.Level.HiHi",
+ State: AlarmState.Active,
+ Priority: 700,
+ Timestamp: DateTimeOffset.UtcNow)
+ {
+ Kind = AlarmKind.NativeOpcUa,
+ Level = AlarmLevel.High,
+ Condition = new AlarmConditionState(
+ Active: true,
+ Acknowledged: false,
+ Confirmed: null,
+ Shelve: AlarmShelveState.Unshelved,
+ Suppressed: false,
+ Severity: 700),
+ };
+
+ [Fact]
+ public void Renders_StateKindSubStateSeverityAndLevel_Badges()
+ {
+ var alarm = ActiveNativeHighUnacked();
+
+ var cut = Render(p => p.Add(c => c.Alarm, alarm));
+
+ // State badge: Active condition tinted danger.
+ Assert.Contains("bg-danger", cut.Markup);
+ Assert.Contains("Active", cut.Markup);
+
+ // Kind badge label for a native OPC UA alarm.
+ Assert.Contains("OPC UA", cut.Markup);
+
+ // Native sub-state badge: active + unacknowledged.
+ Assert.Contains("Unacked", cut.Markup);
+
+ // Severity span on the unified 0โ1000 scale.
+ Assert.Contains("sev 700", cut.Markup);
+
+ // HiLo level badge label.
+ Assert.Contains("Hi", cut.Markup);
+ }
+}