@* Create/edit modal for an equipment-bound scripted alarm, wired straight into IUnsTreeService. The host page owns visibility and supplies the owning equipment id (create) or the loaded ScriptedAlarmEditDto (edit), plus the candidate predicate-script list. The owning equipment is fixed by the tab, so there is no equipment selector. On a successful save it raises OnSaved so the host can refresh the equipment's alarms in place. Lifted from the standalone ScriptedAlarmEdit page (retired in a later task). *@ @using System.ComponentModel.DataAnnotations @using Microsoft.AspNetCore.Components.Forms @using ZB.MOM.WW.OtOpcUa.AdminUI.Uns @inject IUnsTreeService Svc @if (Visible) { } @code { /// Whether the modal is shown. The host owns this flag. [Parameter] public bool Visible { get; set; } /// true to create a new scripted alarm; false to edit . [Parameter] public bool IsNew { get; set; } /// The owning equipment id the created alarm binds to (used only on create). [Parameter] public string? EquipmentId { get; set; } /// The scripted alarm being edited, when is false. [Parameter] public ScriptedAlarmEditDto? Existing { get; set; } /// The candidate predicate scripts as (Id, Display) pairs. [Parameter] public IReadOnlyList<(string Id, string Display)> Scripts { get; set; } = Array.Empty<(string, string)>(); /// Raised after a successful create/save so the host can refresh the equipment's alarms and close. [Parameter] public EventCallback OnSaved { get; set; } /// Raised when the user cancels so the host can close. [Parameter] public EventCallback OnCancel { get; set; } private FormModel _form = new(); private bool _busy; private string? _error; // Tracks which open this modal last loaded for, so unrelated Blazor Server re-renders don't // rebuild _form and clobber in-progress edits. Null while closed. private string? _loadedKey; protected override void OnParametersSet() { if (!Visible) { _loadedKey = null; // closed → next open reloads fresh return; } // Guard against unrelated re-renders. In Blazor Server any live-status push re-invokes // OnParametersSet; without this the rebuild below would silently discard whatever the // operator has typed. Only rebuild when the modal OPENS or the target entity CHANGES. var key = IsNew ? "" : Existing?.ScriptedAlarmId; if (key == _loadedKey) return; // same open, re-render → preserve in-progress form edits _loadedKey = key; // Rebuild the working form whenever the host (re)opens the modal for a fresh target. if (IsNew) { _form = new FormModel(); } else if (Existing is not null) { _form = new FormModel { ScriptedAlarmId = Existing.ScriptedAlarmId, Name = Existing.Name, AlarmType = Existing.AlarmType, Severity = Existing.Severity, MessageTemplate = Existing.MessageTemplate, PredicateScriptId = Existing.PredicateScriptId, HistorizeToAveva = Existing.HistorizeToAveva, Retain = Existing.Retain, Enabled = Existing.Enabled, }; } _error = null; } private async Task SaveAsync() { _busy = true; _error = null; try { var input = new ScriptedAlarmInput(_form.ScriptedAlarmId, _form.Name, _form.AlarmType, _form.Severity, _form.MessageTemplate, _form.PredicateScriptId, _form.HistorizeToAveva, _form.Retain, _form.Enabled); var result = IsNew ? await Svc.CreateScriptedAlarmAsync(EquipmentId!, input) : await Svc.UpdateScriptedAlarmAsync(Existing!.ScriptedAlarmId, input, Existing.RowVersion); if (result.Ok) { await OnSaved.InvokeAsync(); } else { _error = result.Error; } } finally { _busy = false; } } private Task CancelAsync() => OnCancel.InvokeAsync(); private sealed class FormModel { [Required, RegularExpression("^[A-Za-z0-9_-]+$")] public string ScriptedAlarmId { get; set; } = ""; [Required] public string Name { get; set; } = ""; [Required] public string AlarmType { get; set; } = "LimitAlarm"; [Range(1, 1000)] public int Severity { get; set; } = 500; [Required] public string PredicateScriptId { get; set; } = ""; [Required] public string MessageTemplate { get; set; } = ""; public bool HistorizeToAveva { get; set; } = true; public bool Retain { get; set; } = true; public bool Enabled { get; set; } = true; } }