From 7dd4c46cb7a8970c3f452ebc4707392c5ed475dc Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 19 Jan 2026 19:52:30 -0500 Subject: [PATCH] feat(configmanager): add IDialogService interface and AvaloniaDialogService Add dialog service abstraction for platform-specific dialogs: - IDialogService interface with folder picker, message, confirmation, diff preview, and validation results methods - AvaloniaDialogService implementation using MsBox.Avalonia - Basic implementations for ShowDiffPreviewAsync and ShowValidationResultsAsync (full dialogs to be implemented in Tasks 22-23) - Add MessageBox.Avalonia package reference --- .../JdeScoping.ConfigManager.csproj | 1 + .../Services/AvaloniaDialogService.cs | 149 ++++++++++++++++++ .../Services/IDialogService.cs | 45 ++++++ 3 files changed, 195 insertions(+) create mode 100644 NEW/src/Utils/JdeScoping.ConfigManager/Services/AvaloniaDialogService.cs create mode 100644 NEW/src/Utils/JdeScoping.ConfigManager/Services/IDialogService.cs diff --git a/NEW/src/Utils/JdeScoping.ConfigManager/JdeScoping.ConfigManager.csproj b/NEW/src/Utils/JdeScoping.ConfigManager/JdeScoping.ConfigManager.csproj index cb14031..073449e 100644 --- a/NEW/src/Utils/JdeScoping.ConfigManager/JdeScoping.ConfigManager.csproj +++ b/NEW/src/Utils/JdeScoping.ConfigManager/JdeScoping.ConfigManager.csproj @@ -17,6 +17,7 @@ + diff --git a/NEW/src/Utils/JdeScoping.ConfigManager/Services/AvaloniaDialogService.cs b/NEW/src/Utils/JdeScoping.ConfigManager/Services/AvaloniaDialogService.cs new file mode 100644 index 0000000..cbd42e5 --- /dev/null +++ b/NEW/src/Utils/JdeScoping.ConfigManager/Services/AvaloniaDialogService.cs @@ -0,0 +1,149 @@ +using System.Text; +using Avalonia.Controls; +using Avalonia.Platform.Storage; +using MsBox.Avalonia; +using MsBox.Avalonia.Enums; + +namespace JdeScoping.ConfigManager.Services; + +/// +/// Avalonia implementation of IDialogService using MsBox.Avalonia and platform storage. +/// +public class AvaloniaDialogService : IDialogService +{ + private readonly Func _getMainWindow; + + /// + /// Creates a new instance of AvaloniaDialogService. + /// + /// Factory function to get the main window for dialogs. + public AvaloniaDialogService(Func getMainWindow) + { + _getMainWindow = getMainWindow ?? throw new ArgumentNullException(nameof(getMainWindow)); + } + + /// + public async Task ShowFolderPickerAsync(string? title = null) + { + var window = _getMainWindow(); + if (window == null) + return null; + + var folders = await window.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions + { + Title = title ?? "Select Folder", + AllowMultiple = false + }); + + return folders.Count > 0 ? folders[0].Path.LocalPath : null; + } + + /// + public async Task ShowMessageAsync(string title, string message) + { + var box = MessageBoxManager.GetMessageBoxStandard(title, message, ButtonEnum.Ok, Icon.Info); + var window = _getMainWindow(); + if (window != null) + { + await box.ShowWindowDialogAsync(window); + } + else + { + await box.ShowAsync(); + } + } + + /// + public async Task ShowConfirmationAsync(string title, string message) + { + var box = MessageBoxManager.GetMessageBoxStandard(title, message, ButtonEnum.YesNo, Icon.Question); + var window = _getMainWindow(); + ButtonResult result; + if (window != null) + { + result = await box.ShowWindowDialogAsync(window); + } + else + { + result = await box.ShowAsync(); + } + return result == ButtonResult.Yes; + } + + /// + public async Task ShowDiffPreviewAsync(string title, DiffResult diff) + { + // Basic implementation - full diff preview dialog will be implemented in Task 22 + if (!diff.HasChanges) + { + await ShowMessageAsync(title, "No changes detected."); + return false; + } + + var summary = new StringBuilder(); + summary.AppendLine($"Changes detected: {diff.Insertions} insertion(s), {diff.Deletions} deletion(s)"); + summary.AppendLine(); + summary.AppendLine("Do you want to apply these changes?"); + + return await ShowConfirmationAsync(title, summary.ToString()); + } + + /// + public async Task ShowValidationResultsAsync(ValidationResult appSettingsResult, ValidationResult pipelinesResult) + { + // Basic implementation - full validation results dialog will be implemented in Task 23 + var message = new StringBuilder(); + + message.AppendLine("=== AppSettings Validation ==="); + if (appSettingsResult.IsValid) + { + message.AppendLine("Valid"); + } + else + { + foreach (var error in appSettingsResult.Errors) + { + message.AppendLine($"Error: {error}"); + } + } + foreach (var warning in appSettingsResult.Warnings) + { + message.AppendLine($"Warning: {warning}"); + } + + message.AppendLine(); + message.AppendLine("=== Pipelines Validation ==="); + if (pipelinesResult.IsValid) + { + message.AppendLine("Valid"); + } + else + { + foreach (var error in pipelinesResult.Errors) + { + message.AppendLine($"Error: {error}"); + } + } + foreach (var warning in pipelinesResult.Warnings) + { + message.AppendLine($"Warning: {warning}"); + } + + var title = appSettingsResult.IsValid && pipelinesResult.IsValid + ? "Validation Passed" + : "Validation Issues Found"; + + var icon = appSettingsResult.IsValid && pipelinesResult.IsValid ? Icon.Success : Icon.Warning; + + var box = MessageBoxManager.GetMessageBoxStandard(title, message.ToString(), ButtonEnum.Ok, icon); + var window = _getMainWindow(); + if (window != null) + { + await box.ShowWindowDialogAsync(window); + } + else + { + await box.ShowAsync(); + } + } +} diff --git a/NEW/src/Utils/JdeScoping.ConfigManager/Services/IDialogService.cs b/NEW/src/Utils/JdeScoping.ConfigManager/Services/IDialogService.cs new file mode 100644 index 0000000..16786f5 --- /dev/null +++ b/NEW/src/Utils/JdeScoping.ConfigManager/Services/IDialogService.cs @@ -0,0 +1,45 @@ +namespace JdeScoping.ConfigManager.Services; + +/// +/// Abstraction for platform-specific dialog operations. +/// Enables unit testing of view models that need to show dialogs. +/// +public interface IDialogService +{ + /// + /// Shows a folder picker dialog. + /// + /// Optional title for the dialog. + /// The selected folder path, or null if cancelled. + Task ShowFolderPickerAsync(string? title = null); + + /// + /// Shows a message dialog. + /// + /// The dialog title. + /// The message to display. + Task ShowMessageAsync(string title, string message); + + /// + /// Shows a confirmation dialog with Yes/No options. + /// + /// The dialog title. + /// The confirmation message to display. + /// True if user clicked Yes, false otherwise. + Task ShowConfirmationAsync(string title, string message); + + /// + /// Shows a diff preview dialog allowing the user to review changes. + /// + /// The dialog title. + /// The diff result to display. + /// True if user confirms the changes, false otherwise. + Task ShowDiffPreviewAsync(string title, DiffResult diff); + + /// + /// Shows validation results for configuration files. + /// + /// Validation result for appsettings.json. + /// Validation result for pipelines.json. + Task ShowValidationResultsAsync(ValidationResult appSettingsResult, ValidationResult pipelinesResult); +}