docs(06): create phase plan for global site selection (5 plans, 3 waves)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
321
.planning/phases/06-global-site-selection/06-04-PLAN.md
Normal file
321
.planning/phases/06-global-site-selection/06-04-PLAN.md
Normal file
@@ -0,0 +1,321 @@
|
||||
---
|
||||
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>
|
||||
Reference in New Issue
Block a user