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

322 lines
14 KiB
Markdown

---
phase: 06-global-site-selection
plan: 04
type: execute
wave: 2
depends_on: [06-01]
files_modified:
- SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs
- SharepointToolbox/ViewModels/Tabs/StorageViewModel.cs
- SharepointToolbox/ViewModels/Tabs/SearchViewModel.cs
- SharepointToolbox/ViewModels/Tabs/DuplicatesViewModel.cs
- SharepointToolbox/ViewModels/Tabs/FolderStructureViewModel.cs
- SharepointToolbox/ViewModels/Tabs/TransferViewModel.cs
- SharepointToolbox/ViewModels/Tabs/BulkMembersViewModel.cs
autonomous: true
requirements:
- SITE-01
- SITE-02
must_haves:
truths:
- "Permissions tab pre-populates SelectedSites from global sites when no local override exists"
- "Storage, Search, Duplicates, FolderStructure tabs pre-fill SiteUrl from first global site URL"
- "Transfer tab pre-fills SourceSiteUrl from first global site URL"
- "BulkMembers tab does not consume global sites (CSV-driven, no SiteUrl field)"
- "Settings, BulkSites, Templates tabs do not consume global sites (per CONTEXT decisions)"
- "A user can type into a tab's SiteUrl field (local override) without clearing the global state"
- "Global site selection changes update all consuming tabs automatically"
artifacts:
- path: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs"
provides: "Multi-site global consumption — pre-populates SelectedSites"
contains: "OnGlobalSitesChanged"
- path: "SharepointToolbox/ViewModels/Tabs/StorageViewModel.cs"
provides: "Single-site global consumption — pre-fills SiteUrl"
contains: "OnGlobalSitesChanged"
- path: "SharepointToolbox/ViewModels/Tabs/SearchViewModel.cs"
provides: "Single-site global consumption — pre-fills SiteUrl"
contains: "OnGlobalSitesChanged"
- path: "SharepointToolbox/ViewModels/Tabs/DuplicatesViewModel.cs"
provides: "Single-site global consumption — pre-fills SiteUrl"
contains: "OnGlobalSitesChanged"
- path: "SharepointToolbox/ViewModels/Tabs/FolderStructureViewModel.cs"
provides: "Single-site global consumption — pre-fills SiteUrl"
contains: "OnGlobalSitesChanged"
- path: "SharepointToolbox/ViewModels/Tabs/TransferViewModel.cs"
provides: "Single-site global consumption — pre-fills SourceSiteUrl"
contains: "OnGlobalSitesChanged"
key_links:
- from: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs"
to: "SharepointToolbox/ViewModels/FeatureViewModelBase.cs"
via: "Override of OnGlobalSitesChanged virtual method"
pattern: "override.*OnGlobalSitesChanged"
- from: "SharepointToolbox/ViewModels/Tabs/StorageViewModel.cs"
to: "SharepointToolbox/ViewModels/FeatureViewModelBase.cs"
via: "Override of OnGlobalSitesChanged virtual method"
pattern: "override.*OnGlobalSitesChanged"
---
<objective>
Update all consuming tab ViewModels to react to global site selection changes. Multi-site tabs (Permissions) pre-populate their site list; single-site tabs pre-fill their SiteUrl from the first global site. Local overrides take priority at run time.
Purpose: Fulfills SITE-01 (all tabs consume global selection) and SITE-02 (per-tab override without clearing global state).
Output: 6 updated tab ViewModels with OnGlobalSitesChanged overrides.
</objective>
<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>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/06-global-site-selection/06-CONTEXT.md
@.planning/phases/06-global-site-selection/06-01-SUMMARY.md
<interfaces>
<!-- Base class contract from plan 06-01 -->
From SharepointToolbox/ViewModels/FeatureViewModelBase.cs:
```csharp
protected IReadOnlyList<SiteInfo> GlobalSites { get; private set; }
protected virtual void OnGlobalSitesChanged(IReadOnlyList<SiteInfo> sites)
{
// Derived classes override to react to global site changes
}
```
<!-- PermissionsViewModel — multi-site pattern (has SelectedSites collection) -->
From SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs:
```csharp
public ObservableCollection<SiteInfo> SelectedSites { get; } = new();
[ObservableProperty] private string _siteUrl = string.Empty;
// RunOperationAsync uses SelectedSites.Count > 0 ? SelectedSites : SiteUrl
```
<!-- Single-site tab pattern (Storage, Search, Duplicates, FolderStructure) -->
From SharepointToolbox/ViewModels/Tabs/StorageViewModel.cs:
```csharp
[ObservableProperty] private string _siteUrl = string.Empty;
// RunOperationAsync checks string.IsNullOrWhiteSpace(SiteUrl)
```
<!-- Transfer tab pattern (has SourceSiteUrl, not SiteUrl) -->
From SharepointToolbox/ViewModels/Tabs/TransferViewModel.cs:
```csharp
[ObservableProperty] private string _sourceSiteUrl = string.Empty;
```
<!-- Tabs that do NOT consume global sites (no changes needed): -->
<!-- SettingsViewModel — no SiteUrl -->
<!-- BulkSitesViewModel — creates sites from CSV -->
<!-- TemplatesViewModel — creates new sites -->
<!-- BulkMembersViewModel — CSV-driven, no SiteUrl field -->
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Update PermissionsViewModel for multi-site global consumption</name>
<files>SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs</files>
<action>
PermissionsViewModel already supports multi-site via its SelectedSites collection. The global sites should pre-populate SelectedSites when the user has not made a local override.
Add a private field to track whether the user has made a local site selection on this tab:
```csharp
private bool _hasLocalSiteOverride;
```
Override OnGlobalSitesChanged to pre-populate SelectedSites when no local override exists:
```csharp
protected override void OnGlobalSitesChanged(IReadOnlyList<SiteInfo> sites)
{
if (_hasLocalSiteOverride) return;
SelectedSites.Clear();
foreach (var site in sites)
SelectedSites.Add(site);
}
```
In the existing `ExecuteOpenSitePicker` method, set `_hasLocalSiteOverride = true;` after the user picks sites locally. Add this line right before `SelectedSites.Clear()`:
```csharp
private void ExecuteOpenSitePicker()
{
if (OpenSitePickerDialog == null) return;
var dialog = OpenSitePickerDialog.Invoke();
if (dialog?.ShowDialog() == true && dialog is Views.Dialogs.SitePickerDialog picker)
{
_hasLocalSiteOverride = true; // <-- ADD THIS LINE
SelectedSites.Clear();
foreach (var site in picker.SelectedUrls)
SelectedSites.Add(site);
}
}
```
In the existing `OnTenantSwitched` method, reset the local override flag:
```csharp
protected override void OnTenantSwitched(TenantProfile profile)
{
_currentProfile = profile;
_hasLocalSiteOverride = false; // <-- ADD THIS LINE
Results = new ObservableCollection<PermissionEntry>();
SiteUrl = string.Empty;
SelectedSites.Clear();
// ... rest unchanged
}
```
Do NOT modify RunOperationAsync — its existing logic already handles the correct priority: `SelectedSites.Count > 0 ? SelectedSites : SiteUrl`. When global sites are active, SelectedSites will be populated, so it naturally uses global sites. When user picks locally, SelectedSites has the local override.
</action>
<verify>
<automated>cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5</automated>
</verify>
<done>PermissionsViewModel overrides OnGlobalSitesChanged to pre-populate SelectedSites. Local site picker sets _hasLocalSiteOverride=true to prevent global from overwriting. Tenant switch resets the flag.</done>
</task>
<task type="auto">
<name>Task 2: Update single-site tab VMs (Storage, Search, Duplicates, FolderStructure) for global consumption</name>
<files>
SharepointToolbox/ViewModels/Tabs/StorageViewModel.cs,
SharepointToolbox/ViewModels/Tabs/SearchViewModel.cs,
SharepointToolbox/ViewModels/Tabs/DuplicatesViewModel.cs,
SharepointToolbox/ViewModels/Tabs/FolderStructureViewModel.cs
</files>
<action>
All four single-site tabs follow the identical pattern: pre-fill SiteUrl from the first global site when the user has not typed a local URL.
For EACH of these four ViewModels, apply the same changes:
1. Add a using directive if not present:
```csharp
using SharepointToolbox.Core.Models; // for SiteInfo — likely already imported for TenantProfile
```
2. Add a private tracking field (place near other private fields):
```csharp
private bool _hasLocalSiteOverride;
```
3. Override OnGlobalSitesChanged:
```csharp
protected override void OnGlobalSitesChanged(IReadOnlyList<SiteInfo> sites)
{
if (_hasLocalSiteOverride) return;
SiteUrl = sites.Count > 0 ? sites[0].Url : string.Empty;
}
```
4. Detect local override when user modifies SiteUrl. Add a partial method for the [ObservableProperty] SiteUrl change notification:
```csharp
partial void OnSiteUrlChanged(string value)
{
// If the user typed something different from the global site, mark as local override.
// Empty string means user cleared it — revert to global.
if (string.IsNullOrWhiteSpace(value))
{
_hasLocalSiteOverride = false;
// Re-apply global sites if available
if (GlobalSites.Count > 0)
SiteUrl = GlobalSites[0].Url;
}
else if (GlobalSites.Count == 0 || value != GlobalSites[0].Url)
{
_hasLocalSiteOverride = true;
}
}
```
IMPORTANT: Check if any of these VMs already has a `partial void OnSiteUrlChanged` method. If so, merge the logic into the existing method rather than creating a duplicate. Currently:
- StorageViewModel: no OnSiteUrlChanged — add it
- SearchViewModel: no OnSiteUrlChanged — add it
- DuplicatesViewModel: no OnSiteUrlChanged — add it
- FolderStructureViewModel: no OnSiteUrlChanged — add it
5. In the existing `OnTenantSwitched` method of each VM, add `_hasLocalSiteOverride = false;` at the beginning of the method body (after `_currentProfile = profile;`).
Do NOT modify RunOperationAsync in any of these VMs — they already check `string.IsNullOrWhiteSpace(SiteUrl)` and use the value directly. When global sites are active, SiteUrl will be pre-filled, so the existing logic works.
</action>
<verify>
<automated>cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5</automated>
</verify>
<done>StorageViewModel, SearchViewModel, DuplicatesViewModel, and FolderStructureViewModel all override OnGlobalSitesChanged to pre-fill SiteUrl from first global site. Local typing sets _hasLocalSiteOverride=true. Tenant switch resets the flag. Build succeeds.</done>
</task>
<task type="auto">
<name>Task 3: Update TransferViewModel and verify BulkMembersViewModel excluded</name>
<files>
SharepointToolbox/ViewModels/Tabs/TransferViewModel.cs,
SharepointToolbox/ViewModels/Tabs/BulkMembersViewModel.cs
</files>
<action>
**TransferViewModel** — Pre-fill SourceSiteUrl from first global site (same pattern as single-site tabs, but the field is SourceSiteUrl not SiteUrl).
1. Add tracking field:
```csharp
private bool _hasLocalSourceSiteOverride;
```
2. Override OnGlobalSitesChanged:
```csharp
protected override void OnGlobalSitesChanged(IReadOnlyList<SiteInfo> sites)
{
if (_hasLocalSourceSiteOverride) return;
SourceSiteUrl = sites.Count > 0 ? sites[0].Url : string.Empty;
}
```
3. Add partial method for SourceSiteUrl change notification:
```csharp
partial void OnSourceSiteUrlChanged(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
_hasLocalSourceSiteOverride = false;
if (GlobalSites.Count > 0)
SourceSiteUrl = GlobalSites[0].Url;
}
else if (GlobalSites.Count == 0 || value != GlobalSites[0].Url)
{
_hasLocalSourceSiteOverride = true;
}
}
```
4. In the existing `OnTenantSwitched` method, add `_hasLocalSourceSiteOverride = false;` at the beginning.
**BulkMembersViewModel** — Verify it does NOT need changes. BulkMembersViewModel has no SiteUrl field (it reads site URLs from CSV rows). Confirm this by checking: it should NOT have an OnGlobalSitesChanged override. Do NOT modify this file — only verify it has no SiteUrl property.
Note: SettingsViewModel, BulkSitesViewModel, and TemplatesViewModel also do NOT consume global sites per the CONTEXT decisions. Do NOT modify them.
</action>
<verify>
<automated>cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5</automated>
</verify>
<done>TransferViewModel overrides OnGlobalSitesChanged to pre-fill SourceSiteUrl. BulkMembersViewModel is confirmed excluded (no SiteUrl, no override). Build succeeds.</done>
</task>
</tasks>
<verification>
- `dotnet build SharepointToolbox/SharepointToolbox.csproj` succeeds with 0 errors
- `dotnet test` shows no new failures
- PermissionsViewModel has OnGlobalSitesChanged override populating SelectedSites
- StorageViewModel, SearchViewModel, DuplicatesViewModel, FolderStructureViewModel have OnGlobalSitesChanged override setting SiteUrl
- TransferViewModel has OnGlobalSitesChanged override setting SourceSiteUrl
- BulkMembersViewModel, SettingsViewModel, BulkSitesViewModel, TemplatesViewModel are NOT modified
- All consuming VMs have _hasLocalSiteOverride tracking
- All consuming VMs reset the override flag on tenant switch
</verification>
<success_criteria>
Every tab that should consume global sites does so automatically. Multi-site tab (Permissions) pre-populates its SelectedSites collection. Single-site tabs pre-fill their SiteUrl/SourceSiteUrl from the first global site. Users can type a different URL on any tab without clearing the global state. Tabs that don't apply (Settings, BulkSites, Templates, BulkMembers) are unaffected.
</success_criteria>
<output>
After completion, create `.planning/phases/06-global-site-selection/06-04-SUMMARY.md`
</output>