Files
Sharepoint-Toolbox/.planning/phases/06-global-site-selection/06-02-PLAN.md
2026-04-07 09:57:15 +02:00

8.4 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
06-global-site-selection 02 execute 1
SharepointToolbox/ViewModels/MainWindowViewModel.cs
true
SITE-01
truths artifacts key_links
MainWindowViewModel has an ObservableCollection<SiteInfo> GlobalSelectedSites property
OpenGlobalSitePickerCommand opens the site picker dialog and populates GlobalSelectedSites from the result
Changing GlobalSelectedSites broadcasts GlobalSitesChangedMessage via WeakReferenceMessenger
Switching tenant profiles clears GlobalSelectedSites
Clearing session clears GlobalSelectedSites
OpenGlobalSitePickerCommand is disabled when no profile is selected
path provides contains
SharepointToolbox/ViewModels/MainWindowViewModel.cs Global site selection state, command, and message broadcast GlobalSelectedSites
from to via pattern
SharepointToolbox/ViewModels/MainWindowViewModel.cs SharepointToolbox/Core/Messages/GlobalSitesChangedMessage.cs WeakReferenceMessenger.Default.Send in GlobalSelectedSites setter Send.*GlobalSitesChangedMessage
Add global site selection state and command to MainWindowViewModel. This VM owns the global site list, broadcasts changes via GlobalSitesChangedMessage, and clears the selection on tenant switch and session clear.

Purpose: Central state management for global site selection — the toolbar UI (plan 06-03) binds to these properties. Output: Updated MainWindowViewModel.cs with GlobalSelectedSites, OpenGlobalSitePickerCommand, GlobalSitesSelectedLabel, and broadcast logic.

<execution_context> @C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md @C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/06-global-site-selection/06-CONTEXT.md From SharepointToolbox/ViewModels/MainWindowViewModel.cs: ```csharp public partial class MainWindowViewModel : ObservableRecipient { // Existing — DO NOT MODIFY public Func? OpenProfileManagementDialog { get; set; } public ObservableCollection TenantProfiles { get; } public IAsyncRelayCommand ConnectCommand { get; } public IAsyncRelayCommand ClearSessionCommand { get; } public RelayCommand ManageProfilesCommand { get; }
// OnSelectedProfileChanged sends TenantSwitchedMessage
// ClearSessionAsync clears session

}


From SharepointToolbox/Core/Models/SiteInfo.cs:
```csharp
public record SiteInfo(string Url, string Title);

From SharepointToolbox/Views/Tabs/PermissionsView.xaml.cs:

vm.OpenSitePickerDialog = () =>
{
    var factory = serviceProvider.GetRequiredService<Func<TenantProfile, SitePickerDialog>>();
    return factory(vm.CurrentProfile ?? new TenantProfile());
};

From SharepointToolbox/Views/Dialogs/SitePickerDialog.xaml.cs:

public IReadOnlyList<SiteInfo> SelectedUrls =>
    _allItems.Where(i => i.IsSelected).Select(i => new SiteInfo(i.Url, i.Title)).ToList();
// DialogResult = true on OK click
Task 1: Add global site selection state, command, and broadcast to MainWindowViewModel SharepointToolbox/ViewModels/MainWindowViewModel.cs Modify MainWindowViewModel to add global site selection support. All changes are additive — do not remove or modify any existing properties/methods except where noted.
1. Add using directives at the top (if not already present):
```csharp
using SharepointToolbox.Core.Models;  // for SiteInfo — may already be there for TenantProfile
```

2. Add a dialog factory property (same pattern as OpenProfileManagementDialog). Place it near the other dialog factory:
```csharp
/// <summary>
/// Factory set by MainWindow.xaml.cs to open the SitePickerDialog for global site selection.
/// Returns the opened Window; ViewModel calls ShowDialog() on it.
/// </summary>
public Func<Window>? OpenGlobalSitePickerDialog { get; set; }
```

3. Add the global site selection collection and label. Place after existing observable properties:
```csharp
public ObservableCollection<SiteInfo> GlobalSelectedSites { get; } = new();

/// <summary>
/// Label for toolbar display: "3 site(s) selected" or "No sites selected".
/// </summary>
public string GlobalSitesSelectedLabel =>
    GlobalSelectedSites.Count > 0
        ? $"{GlobalSelectedSites.Count} site(s) selected"
        : "No sites selected";
```

Note: The label uses a hardcoded string for now. Plan 06-03 will replace it with a localized string once the localization keys are added.

4. Add the command. Declare it near the other commands:
```csharp
public RelayCommand OpenGlobalSitePickerCommand { get; }
```

5. In the constructor, initialize the command (after ManageProfilesCommand initialization):
```csharp
OpenGlobalSitePickerCommand = new RelayCommand(ExecuteOpenGlobalSitePicker, () => SelectedProfile != null);
GlobalSelectedSites.CollectionChanged += (_, _) =>
{
    OnPropertyChanged(nameof(GlobalSitesSelectedLabel));
    BroadcastGlobalSites();
};
```

6. Add the command implementation method:
```csharp
private void ExecuteOpenGlobalSitePicker()
{
    if (OpenGlobalSitePickerDialog == null) return;
    var dialog = OpenGlobalSitePickerDialog.Invoke();
    if (dialog?.ShowDialog() == true && dialog is Views.Dialogs.SitePickerDialog picker)
    {
        GlobalSelectedSites.Clear();
        foreach (var site in picker.SelectedUrls)
            GlobalSelectedSites.Add(site);
    }
}
```

7. Add the broadcast helper method:
```csharp
private void BroadcastGlobalSites()
{
    WeakReferenceMessenger.Default.Send(
        new GlobalSitesChangedMessage(GlobalSelectedSites.ToList().AsReadOnly()));
}
```

8. In `OnSelectedProfileChanged`, add after the existing body:
```csharp
// Clear global site selection on tenant switch (sites belong to a tenant)
GlobalSelectedSites.Clear();
OpenGlobalSitePickerCommand.NotifyCanExecuteChanged();
```

9. In `ClearSessionAsync`, add at the END of the try block (before ConnectionStatus = "Not connected"):
```csharp
GlobalSelectedSites.Clear();
```

10. Add required using for the message (if not already imported):
```csharp
using SharepointToolbox.Core.Messages;  // already present for TenantSwitchedMessage
```

IMPORTANT: The `using SharepointToolbox.Views.Dialogs;` namespace is needed for the `SitePickerDialog` cast in ExecuteOpenGlobalSitePicker. Add it if not present. This is acceptable since MainWindowViewModel already references `System.Windows.Window` (a View-layer type) via the dialog factory pattern.
cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5 MainWindowViewModel compiles with GlobalSelectedSites collection, OpenGlobalSitePickerCommand (disabled when no profile), GlobalSitesSelectedLabel, broadcast on collection change, and clear on tenant switch + session clear. - `dotnet build SharepointToolbox/SharepointToolbox.csproj` succeeds with 0 errors - MainWindowViewModel.cs contains GlobalSelectedSites ObservableCollection - MainWindowViewModel.cs contains OpenGlobalSitePickerCommand - MainWindowViewModel.cs contains GlobalSitesSelectedLabel property - MainWindowViewModel.cs sends GlobalSitesChangedMessage when collection changes - OnSelectedProfileChanged clears GlobalSelectedSites - ClearSessionAsync clears GlobalSelectedSites

<success_criteria> MainWindowViewModel owns the global site selection state, can open the site picker dialog, broadcasts changes to all tab VMs, and clears the selection on tenant switch and session clear. The toolbar UI (plan 06-03) can bind directly to these properties and commands. </success_criteria>

After completion, create `.planning/phases/06-global-site-selection/06-02-SUMMARY.md`