322 lines
14 KiB
Markdown
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>
|