From 80b9ab2d575fb2d760672ba532ce24714b79729e Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 21 Aug 2026 06:12:53 -0400 Subject: [PATCH] feat(centralui): Create saves bindings (upsert) and lands on Configure with a created/bound/bindError notice Claude-Session: https://claude.ai/code/session_01G2GHPnbbKGvyyfbr2XBRVH --- .../Pages/Deployment/InstanceConfigure.razor | 29 ++++++++++++ .../Pages/Deployment/InstanceCreate.razor | 46 ++++++++++++++++--- 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/InstanceConfigure.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/InstanceConfigure.razor index 5cdcf959..0a940e31 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/InstanceConfigure.razor +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/InstanceConfigure.razor @@ -81,6 +81,28 @@ + @* One-shot notice for an instance that just arrived from the Create page + (design 2026-08-21). Query-string only — nothing is persisted, so it + disappears on any later visit. *@ + @if (Created == 1) + { +
+ Instance created. + @if (BindError is not null) + { + Bindings were NOT saved: @BindError — bind the groups below. + } + else if (Bound is > 0) + { + @Bound binding(s) saved. Test Bindings, then Deploy. + } + else + { + Bind the groups below, then Deploy. + } +
+ } + @* Connection Bindings — grouped by module (design 2026-08-20). Rows come from the flattened configuration so composed module members appear; each group header carries a connection + Object apply that rewrites @@ -710,6 +732,13 @@ @code { [Parameter] public int Id { get; set; } + // Set by the Create Instance page's post-create redirect (design 2026-08-21): + // Created=1 always, then either the count of bindings saved or the error that + // stopped them being saved. Read-only display — never persisted. + [SupplyParameterFromQuery] public int? Created { get; set; } + [SupplyParameterFromQuery] public int? Bound { get; set; } + [SupplyParameterFromQuery] public string? BindError { get; set; } + private Instance? _instance; private string _templateName = ""; private string _siteName = ""; diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/InstanceCreate.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/InstanceCreate.razor index 3c0e6f2e..34c43e75 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/InstanceCreate.razor +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/InstanceCreate.razor @@ -293,26 +293,60 @@ return; } + string user; + int id; try { - var user = await GetCurrentUserAsync(); + user = await GetCurrentUserAsync(); var result = await InstanceService.CreateInstanceAsync( _createName.Trim(), _createTemplateId, _createSiteId, _createAreaId == 0 ? null : _createAreaId, user); - if (result.IsSuccess) - { - NavigationManager.NavigateTo("/deployment/topology"); - } - else + if (result.IsFailure) { _formError = result.Error; + return; } + id = result.Value.Id; } catch (Exception ex) { _formError = $"Create failed: {ex.Message}"; + return; } + + // From here the instance EXISTS. Whatever the binding save does, land on + // Configure rather than leaving the operator on a stale create form — + // carrying any binding error in the query so it is visible there. + var bindings = _skipBindings || _previewError is not null + ? (IReadOnlyList)Array.Empty() + : CreateBindingPlanner.BuildBindings(_groups, _inputs); + + if (bindings.Count == 0) + { + NavigationManager.NavigateTo($"/deployment/instances/{id}/configure?created=1"); + return; + } + + string? bindError = null; + try + { + var bind = await InstanceService.SetConnectionBindingsAsync(id, bindings, user); + if (bind.IsFailure) bindError = bind.Error; + } + catch (Exception ex) + { + // Network / actor timeout AFTER the instance was created. + bindError = ex.Message; + } + + var query = bindError is null + ? $"created=1&bound={bindings.Count}" + : $"created=1&bindError={Uri.EscapeDataString(Truncate(bindError, 300))}"; + NavigationManager.NavigateTo($"/deployment/instances/{id}/configure?{query}"); } + /// Keeps a failure message short enough to ride in the query string. + private static string Truncate(string s, int n) => s.Length <= n ? s : s[..n] + "…"; + private void GoBack() => NavigationManager.NavigateTo("/deployment/topology"); // CentralUI-024: delegates to the shared helper so the claim type stays