@page "/design/schema-library" @using ZB.MOM.WW.ScadaBridge.Security @using ZB.MOM.WW.ScadaBridge.CentralUI.Services @using ZB.MOM.WW.ScadaBridge.Commons.Entities.Schemas @attribute [Authorize(Policy = AuthorizationPolicies.RequireDesign)] @inject ISchemaLibraryService SchemaLibraryService @inject IDialogService Dialog @* Schema Library: list + create/edit (via SchemaBuilder) + delete the reusable named JSON-Schema library entries that the {"$ref":"lib:Name"} resolver resolves against. Every mutation is dispatched through ISchemaLibraryService — the guard-running ManagementActor path — never a direct repo write. *@

Schema Library

@if (!_editing) { }
@if (_editing) {
@(_editId.HasValue ? $"Edit Schema: {_editOriginalName}" : "New Schema")
Referenced as lib:@(string.IsNullOrWhiteSpace(_formName) ? "Name" : _formName.Trim()).
@if (_formError != null) {
@_formError
}
} @if (_loading) { } else if (_schemas.Count == 0) {

No library schemas defined.

@if (!_editing) { }
} else { @foreach (var s in _schemas) { }
Name Scope Reference Actions
@s.Name @(string.IsNullOrWhiteSpace(s.Scope) ? "—" : s.Scope) lib:@s.Name @* Row actions are disabled while the editor is open so the row under edit (and its siblings) can't be deleted out from under the form, and while a delete is in flight (_busy). *@
}
@code { private List _schemas = new(); private bool _loading = true; private bool _busy; private bool _editing; private int? _editId; private string _editOriginalName = string.Empty; private string _formName = string.Empty; private string? _formScope; private string? _formSchemaJson; private string? _formError; private ToastNotification _toast = default!; protected override async Task OnInitializedAsync() { await LoadAsync(); } private async Task LoadAsync() { _loading = true; try { _schemas = (await SchemaLibraryService.ListAsync()).ToList(); } finally { _loading = false; } } private void BeginCreate() { _editing = true; _editId = null; _editOriginalName = string.Empty; _formName = string.Empty; _formScope = null; // Seed an object-shaped schema so the builder opens with a useful default. _formSchemaJson = """{"type":"object","properties":{}}"""; _formError = null; } private void BeginEdit(SharedSchema schema) { _editing = true; _editId = schema.Id; _editOriginalName = schema.Name; _formName = schema.Name; _formScope = schema.Scope; _formSchemaJson = schema.SchemaJson; _formError = null; } private void CancelEdit() { _editing = false; _formError = null; } private async Task SaveSchema() { _formError = null; var name = _formName?.Trim() ?? string.Empty; if (string.IsNullOrEmpty(name)) { _formError = "Schema name is required."; return; } var scope = string.IsNullOrWhiteSpace(_formScope) ? null : _formScope.Trim(); var schemaJson = _formSchemaJson ?? string.Empty; _busy = true; try { var result = _editId.HasValue ? await SchemaLibraryService.UpdateAsync(_editId.Value, name, scope, schemaJson) : await SchemaLibraryService.CreateAsync(name, scope, schemaJson); if (result.Success) { _toast.ShowSuccess(_editId.HasValue ? $"Schema '{name}' updated." : $"Schema '{name}' created."); _editing = false; await LoadAsync(); } else { _formError = result.Error; } } finally { _busy = false; } } private async Task DeleteSchema(SharedSchema schema) { // In-flight guard: an editor-open row action is already disabled in the markup, // but the _busy gate is the authoritative guard against a double-invoked delete // (and mirrors the Save path's guard). if (_busy) return; var confirmed = await Dialog.ConfirmAsync( "Delete Schema", $"Delete library schema '{schema.Name}'? References to lib:{schema.Name} will no longer resolve.", danger: true); if (!confirmed) return; _busy = true; try { var result = await SchemaLibraryService.DeleteAsync(schema.Id); if (result.Success) { _toast.ShowSuccess($"Schema '{schema.Name}' deleted."); await LoadAsync(); } else { _toast.ShowError(result.Error ?? "Delete failed."); } } finally { _busy = false; } } }