diff --git a/SharepointToolbox/ViewModels/MainWindowViewModel.cs b/SharepointToolbox/ViewModels/MainWindowViewModel.cs index 7b2b7e6..998b681 100644 --- a/SharepointToolbox/ViewModels/MainWindowViewModel.cs +++ b/SharepointToolbox/ViewModels/MainWindowViewModel.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.Logging; using SharepointToolbox.Core.Messages; using SharepointToolbox.Core.Models; using SharepointToolbox.Services; +using SharepointToolbox.Views.Dialogs; namespace SharepointToolbox.ViewModels; @@ -19,6 +20,12 @@ public partial class MainWindowViewModel : ObservableRecipient // Set by the view layer (MainWindow.xaml.cs) to open the dialog using DI public Func? OpenProfileManagementDialog { get; set; } + /// + /// Factory set by MainWindow.xaml.cs to open the SitePickerDialog for global site selection. + /// Returns the opened Window; ViewModel calls ShowDialog() on it. + /// + public Func? OpenGlobalSitePickerDialog { get; set; } + [ObservableProperty] private TenantProfile? _selectedProfile; @@ -33,9 +40,20 @@ public partial class MainWindowViewModel : ObservableRecipient public ObservableCollection TenantProfiles { get; } = new(); + public ObservableCollection GlobalSelectedSites { get; } = new(); + + /// + /// Label for toolbar display: "3 site(s) selected" or "No sites selected". + /// + public string GlobalSitesSelectedLabel => + GlobalSelectedSites.Count > 0 + ? $"{GlobalSelectedSites.Count} site(s) selected" + : "No sites selected"; + public IAsyncRelayCommand ConnectCommand { get; } public IAsyncRelayCommand ClearSessionCommand { get; } public RelayCommand ManageProfilesCommand { get; } + public RelayCommand OpenGlobalSitePickerCommand { get; } public MainWindowViewModel( ProfileService profileService, @@ -49,6 +67,12 @@ public partial class MainWindowViewModel : ObservableRecipient ConnectCommand = new AsyncRelayCommand(ConnectAsync, () => SelectedProfile != null); ClearSessionCommand = new AsyncRelayCommand(ClearSessionAsync, () => SelectedProfile != null); ManageProfilesCommand = new RelayCommand(OpenProfileManagement); + OpenGlobalSitePickerCommand = new RelayCommand(ExecuteOpenGlobalSitePicker, () => SelectedProfile != null); + GlobalSelectedSites.CollectionChanged += (_, _) => + { + OnPropertyChanged(nameof(GlobalSitesSelectedLabel)); + BroadcastGlobalSites(); + }; IsActive = true; } @@ -74,6 +98,9 @@ public partial class MainWindowViewModel : ObservableRecipient } ConnectCommand.NotifyCanExecuteChanged(); ClearSessionCommand.NotifyCanExecuteChanged(); + // Clear global site selection on tenant switch (sites belong to a tenant) + GlobalSelectedSites.Clear(); + OpenGlobalSitePickerCommand.NotifyCanExecuteChanged(); } public async Task LoadProfilesAsync() @@ -116,6 +143,7 @@ public partial class MainWindowViewModel : ObservableRecipient try { await _sessionManager.ClearSessionAsync(SelectedProfile.TenantUrl); + GlobalSelectedSites.Clear(); ConnectionStatus = "Not connected"; } catch (Exception ex) @@ -134,4 +162,22 @@ public partial class MainWindowViewModel : ObservableRecipient // Reload profiles after dialog closes (modal — ShowDialog blocks until closed) _ = LoadProfilesAsync(); } + + private void ExecuteOpenGlobalSitePicker() + { + if (OpenGlobalSitePickerDialog == null) return; + var dialog = OpenGlobalSitePickerDialog.Invoke(); + if (dialog?.ShowDialog() == true && dialog is SitePickerDialog picker) + { + GlobalSelectedSites.Clear(); + foreach (var site in picker.SelectedUrls) + GlobalSelectedSites.Add(site); + } + } + + private void BroadcastGlobalSites() + { + WeakReferenceMessenger.Default.Send( + new GlobalSitesChangedMessage(GlobalSelectedSites.ToList().AsReadOnly())); + } }