From f2e73107d1f412fd6480f3b7d1cef8b125af5e0c Mon Sep 17 00:00:00 2001 From: Alex Hope-O'Connor Date: Sat, 7 Mar 2026 22:13:31 +1000 Subject: [PATCH] Effective config: editable badge in view mode, refresh on continue/stop after save failure - Multi-value items: EditableAsButton=true so 'editable' badge is clickable in view mode and activates the field like other editable badges. - Save modal: 'Continue editing' after ValidateFailed/ReloadFailed now discards session, refreshes config from disk, then re-enters edit mode so remove/UI work correctly. - 'Stop editing' after ValidateFailed/ReloadFailed now triggers config refresh so the invalid value on disk is shown instead of stale UI. - SaveFlow: HandleCloseAndEndEdit invokes OnSaveCompleted when in failed state; OnContinueEditing uses EventCallback.Factory.Create to fix method-group conversion warning. --- .../Composition/EffectiveConfigSection.razor | 11 ++++++++- .../EffectiveConfigMultiValueItem.razor | 2 +- .../Modals/EffectiveConfigSaveFlow.razor | 23 ++++++++++++++++++- .../Modals/EffectiveConfigSaveModal.razor | 9 +++++++- 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor b/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor index 2bb4c63..06979dc 100644 --- a/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor +++ b/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor @@ -28,7 +28,7 @@ + OnClose="CloseSaveModal" OnSaveCompleted="OnSaveCompleted" OnContinueEditingAfterSavedFailure="ContinueEditingAfterSavedFailureAsync" OnCloseAndEndEdit="CloseSaveModalAndEndEdit" /> } @@ -212,6 +212,15 @@ StateHasChanged(); } + private async Task ContinueEditingAfterSavedFailureAsync() + { + _showSaveModal = false; + Session.ExitEditModeDiscard(); + await OnSaveCompleted.InvokeAsync(); + Session.EnterEditMode(); + StateHasChanged(); + } + private void CloseSaveModalAndEndEdit() { _showSaveModal = false; diff --git a/src/DnsmasqWebUI/Components/EffectiveConfig/Fields/EffectiveConfigMultiValueItem.razor b/src/DnsmasqWebUI/Components/EffectiveConfig/Fields/EffectiveConfigMultiValueItem.razor index 5574ff6..80a4cbe 100644 --- a/src/DnsmasqWebUI/Components/EffectiveConfig/Fields/EffectiveConfigMultiValueItem.razor +++ b/src/DnsmasqWebUI/Components/EffectiveConfig/Fields/EffectiveConfigMultiValueItem.razor @@ -6,7 +6,7 @@ @Value diff --git a/src/DnsmasqWebUI/Components/EffectiveConfig/Modals/EffectiveConfigSaveFlow.razor b/src/DnsmasqWebUI/Components/EffectiveConfig/Modals/EffectiveConfigSaveFlow.razor index 90442b9..3265893 100644 --- a/src/DnsmasqWebUI/Components/EffectiveConfig/Modals/EffectiveConfigSaveFlow.razor +++ b/src/DnsmasqWebUI/Components/EffectiveConfig/Modals/EffectiveConfigSaveFlow.razor @@ -16,10 +16,11 @@ SaveResult="@_saveResult" RestoreResult="@_restoreResult" OnClose="@OnClose" + OnContinueEditing="@(EventCallback.Factory.Create(this, HandleContinueEditing))" OnSave="@HandleSave" OnRestore="@HandleRestore" OnReverted="@HandleReverted" - OnCloseAndEndEdit="@OnCloseAndEndEdit" /> + OnCloseAndEndEdit="@HandleCloseAndEndEdit" /> } @code { @@ -39,6 +40,7 @@ [Parameter] public DnsmasqServiceStatus? Status { get; set; } [Parameter] public EventCallback OnClose { get; set; } [Parameter] public EventCallback OnSaveCompleted { get; set; } + [Parameter] public EventCallback OnContinueEditingAfterSavedFailure { get; set; } [Parameter] public EventCallback OnCloseAndEndEdit { get; set; } [Parameter] public IEffectiveConfigEditSession Session { get; set; } = null!; @@ -127,4 +129,23 @@ _saveState = SaveUiState.RestoreFailed; await InvokeAsync(StateHasChanged); } + + private async Task HandleContinueEditing() + { + if (_saveState == SaveUiState.ValidateFailed || _saveState == SaveUiState.ReloadFailed) + { + await OnContinueEditingAfterSavedFailure.InvokeAsync(); + return; + } + + await OnClose.InvokeAsync(); + } + + private async Task HandleCloseAndEndEdit() + { + await OnCloseAndEndEdit.InvokeAsync(); + + if (_saveState == SaveUiState.ValidateFailed || _saveState == SaveUiState.ReloadFailed) + await OnSaveCompleted.InvokeAsync(); + } } diff --git a/src/DnsmasqWebUI/Components/EffectiveConfig/Modals/EffectiveConfigSaveModal.razor b/src/DnsmasqWebUI/Components/EffectiveConfig/Modals/EffectiveConfigSaveModal.razor index 3f8e1ae..f7b7053 100644 --- a/src/DnsmasqWebUI/Components/EffectiveConfig/Modals/EffectiveConfigSaveModal.razor +++ b/src/DnsmasqWebUI/Components/EffectiveConfig/Modals/EffectiveConfigSaveModal.razor @@ -153,7 +153,7 @@ @if ((SaveState == EffectiveConfigSaveFlow.SaveUiState.ReloadFailed || SaveState == EffectiveConfigSaveFlow.SaveUiState.ValidateFailed) && SaveResult?.BackupPath != null) { - + } else if (SaveState == EffectiveConfigSaveFlow.SaveUiState.RestoreFailed) @@ -184,6 +184,7 @@ [Parameter] public EffectiveConfigSaveResult? SaveResult { get; set; } [Parameter] public EffectiveConfigRestoreResult? RestoreResult { get; set; } [Parameter] public EventCallback OnClose { get; set; } + [Parameter] public EventCallback OnContinueEditing { get; set; } [Parameter] public EventCallback OnSave { get; set; } [Parameter] public EventCallback OnRestore { get; set; } [Parameter] public EventCallback OnReverted { get; set; } @@ -262,6 +263,12 @@ await OnClose.InvokeAsync(); } + private async Task ContinueEditing() + { + if (!AllowClose) return; + await OnContinueEditing.InvokeAsync(); + } + private async Task Save() { await OnSave.InvokeAsync();