docs(08-simplified-permissions): create phase plan (6 plans, 5 waves)
Plans cover plain-language permission labels, risk-level color coding, summary counts, detail-level toggle, export integration, and unit tests. PermissionEntry record is NOT modified — uses wrapper pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
265
.planning/phases/08-simplified-permissions/08-02-PLAN.md
Normal file
265
.planning/phases/08-simplified-permissions/08-02-PLAN.md
Normal file
@@ -0,0 +1,265 @@
|
||||
---
|
||||
phase: 08-simplified-permissions
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: ["08-01"]
|
||||
files_modified:
|
||||
- SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs
|
||||
autonomous: true
|
||||
requirements:
|
||||
- SIMP-01
|
||||
- SIMP-02
|
||||
- SIMP-03
|
||||
must_haves:
|
||||
truths:
|
||||
- "IsSimplifiedMode toggle switches between raw and simplified permission labels in the DataGrid"
|
||||
- "IsDetailView toggle controls whether individual rows are shown or collapsed into summary rows"
|
||||
- "Toggling modes does NOT re-run the scan — it re-renders from existing Results data"
|
||||
- "Summary counts per risk level are available as observable properties when simplified mode is on"
|
||||
- "SimplifiedResults collection is computed from Results whenever Results changes or mode toggles"
|
||||
- "ActiveItemsSource provides the correct collection for DataGrid binding depending on current mode"
|
||||
artifacts:
|
||||
- path: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs"
|
||||
provides: "Extended PermissionsViewModel with simplified mode, detail toggle, and summary"
|
||||
contains: "IsSimplifiedMode"
|
||||
key_links:
|
||||
- from: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs"
|
||||
to: "SharepointToolbox/Core/Helpers/PermissionLevelMapping.cs"
|
||||
via: "SimplifiedPermissionEntry.WrapAll uses PermissionLevelMapping internally"
|
||||
pattern: "SimplifiedPermissionEntry\\.WrapAll"
|
||||
- from: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs"
|
||||
to: "SharepointToolbox/Core/Models/PermissionSummary.cs"
|
||||
via: "PermissionSummaryBuilder.Build computes summary from simplified entries"
|
||||
pattern: "PermissionSummaryBuilder\\.Build"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Extend PermissionsViewModel with IsSimplifiedMode toggle, IsDetailView toggle, SimplifiedResults collection, summary statistics, and an ActiveItemsSource that the DataGrid binds to. All toggles re-render from cached data — no re-scan required.
|
||||
|
||||
Purpose: This is the ViewModel logic for all three SIMP requirements. The View (08-03) binds to these new properties.
|
||||
Output: Updated PermissionsViewModel.cs
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/dev/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@C:/Users/dev/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/phases/08-simplified-permissions/08-01-SUMMARY.md
|
||||
|
||||
<interfaces>
|
||||
<!-- From 08-01: New types this plan consumes -->
|
||||
From SharepointToolbox/Core/Models/RiskLevel.cs:
|
||||
```csharp
|
||||
public enum RiskLevel { High, Medium, Low, ReadOnly }
|
||||
```
|
||||
|
||||
From SharepointToolbox/Core/Helpers/PermissionLevelMapping.cs:
|
||||
```csharp
|
||||
public static class PermissionLevelMapping
|
||||
{
|
||||
public record MappingResult(string Label, RiskLevel RiskLevel);
|
||||
public static MappingResult GetMapping(string roleName);
|
||||
public static IReadOnlyList<MappingResult> GetMappings(string permissionLevels);
|
||||
public static RiskLevel GetHighestRisk(string permissionLevels);
|
||||
public static string GetSimplifiedLabels(string permissionLevels);
|
||||
}
|
||||
```
|
||||
|
||||
From SharepointToolbox/Core/Models/SimplifiedPermissionEntry.cs:
|
||||
```csharp
|
||||
public class SimplifiedPermissionEntry
|
||||
{
|
||||
public PermissionEntry Inner { get; }
|
||||
public string SimplifiedLabels { get; }
|
||||
public RiskLevel RiskLevel { get; }
|
||||
public IReadOnlyList<PermissionLevelMapping.MappingResult> Mappings { get; }
|
||||
// Passthrough: ObjectType, Title, Url, HasUniquePermissions, Users, UserLogins,
|
||||
// PermissionLevels, GrantedThrough, PrincipalType
|
||||
public static IReadOnlyList<SimplifiedPermissionEntry> WrapAll(IEnumerable<PermissionEntry> entries);
|
||||
}
|
||||
```
|
||||
|
||||
From SharepointToolbox/Core/Models/PermissionSummary.cs:
|
||||
```csharp
|
||||
public record PermissionSummary(string Label, RiskLevel RiskLevel, int Count, int DistinctUsers);
|
||||
|
||||
public static class PermissionSummaryBuilder
|
||||
{
|
||||
public static IReadOnlyList<PermissionSummary> Build(IEnumerable<SimplifiedPermissionEntry> entries);
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Current PermissionsViewModel — the file being modified -->
|
||||
From SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs:
|
||||
```csharp
|
||||
public partial class PermissionsViewModel : FeatureViewModelBase
|
||||
{
|
||||
// Existing fields and services — unchanged
|
||||
[ObservableProperty] private ObservableCollection<PermissionEntry> _results = new();
|
||||
|
||||
// Existing commands — unchanged
|
||||
public IAsyncRelayCommand ExportCsvCommand { get; }
|
||||
public IAsyncRelayCommand ExportHtmlCommand { get; }
|
||||
public RelayCommand OpenSitePickerCommand { get; }
|
||||
|
||||
// Full constructor and test constructor (internal)
|
||||
}
|
||||
```
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Add simplified mode properties and summary computation to PermissionsViewModel</name>
|
||||
<files>SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs</files>
|
||||
<action>
|
||||
Modify `SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs` to add simplified mode support. Add the following new using statements at the top:
|
||||
|
||||
```csharp
|
||||
using SharepointToolbox.Core.Helpers;
|
||||
```
|
||||
|
||||
Add these new observable properties to the class (in the "Observable properties" section):
|
||||
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// When true, displays simplified plain-language labels instead of raw SharePoint role names.
|
||||
/// Toggling does not re-run the scan.
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool _isSimplifiedMode;
|
||||
|
||||
/// <summary>
|
||||
/// When true, shows individual item-level rows (detailed view).
|
||||
/// When false, shows only summary rows grouped by risk level (simple view).
|
||||
/// Only meaningful when IsSimplifiedMode is true.
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool _isDetailView = true;
|
||||
```
|
||||
|
||||
Add these computed collection properties (NOT ObservableProperty — manually raised):
|
||||
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// Simplified wrappers computed from Results. Rebuilt when Results changes.
|
||||
/// </summary>
|
||||
private IReadOnlyList<SimplifiedPermissionEntry> _simplifiedResults = Array.Empty<SimplifiedPermissionEntry>();
|
||||
public IReadOnlyList<SimplifiedPermissionEntry> SimplifiedResults
|
||||
{
|
||||
get => _simplifiedResults;
|
||||
private set => SetProperty(ref _simplifiedResults, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Summary counts grouped by risk level. Rebuilt when SimplifiedResults changes.
|
||||
/// </summary>
|
||||
private IReadOnlyList<PermissionSummary> _summaries = Array.Empty<PermissionSummary>();
|
||||
public IReadOnlyList<PermissionSummary> Summaries
|
||||
{
|
||||
get => _summaries;
|
||||
private set => SetProperty(ref _summaries, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The collection the DataGrid actually binds to. Returns:
|
||||
/// - Results (raw) when simplified mode is OFF
|
||||
/// - SimplifiedResults when simplified mode is ON and detail view is ON
|
||||
/// - (View handles summary display separately via Summaries property)
|
||||
/// </summary>
|
||||
public object ActiveItemsSource => IsSimplifiedMode
|
||||
? (object)SimplifiedResults
|
||||
: Results;
|
||||
```
|
||||
|
||||
Add partial methods triggered by property changes:
|
||||
|
||||
```csharp
|
||||
partial void OnIsSimplifiedModeChanged(bool value)
|
||||
{
|
||||
if (value && Results.Count > 0)
|
||||
RebuildSimplifiedData();
|
||||
OnPropertyChanged(nameof(ActiveItemsSource));
|
||||
}
|
||||
|
||||
partial void OnIsDetailViewChanged(bool value)
|
||||
{
|
||||
OnPropertyChanged(nameof(ActiveItemsSource));
|
||||
}
|
||||
```
|
||||
|
||||
Add a private method to rebuild simplified data from existing Results:
|
||||
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// Recomputes SimplifiedResults and Summaries from the current Results collection.
|
||||
/// Called when Results changes or when simplified mode is toggled on.
|
||||
/// </summary>
|
||||
private void RebuildSimplifiedData()
|
||||
{
|
||||
SimplifiedResults = SimplifiedPermissionEntry.WrapAll(Results);
|
||||
Summaries = PermissionSummaryBuilder.Build(SimplifiedResults);
|
||||
}
|
||||
```
|
||||
|
||||
Modify the existing `RunOperationAsync` method: after the line that sets `Results = new ObservableCollection<PermissionEntry>(allEntries);` (both in the dispatcher branch and the else branch), add:
|
||||
|
||||
```csharp
|
||||
if (IsSimplifiedMode)
|
||||
RebuildSimplifiedData();
|
||||
OnPropertyChanged(nameof(ActiveItemsSource));
|
||||
```
|
||||
|
||||
So the end of RunOperationAsync becomes (both branches):
|
||||
```csharp
|
||||
Results = new ObservableCollection<PermissionEntry>(allEntries);
|
||||
if (IsSimplifiedMode)
|
||||
RebuildSimplifiedData();
|
||||
OnPropertyChanged(nameof(ActiveItemsSource));
|
||||
```
|
||||
|
||||
Modify `OnTenantSwitched` to also reset simplified state:
|
||||
After `Results = new ObservableCollection<PermissionEntry>();` add:
|
||||
```csharp
|
||||
SimplifiedResults = Array.Empty<SimplifiedPermissionEntry>();
|
||||
Summaries = Array.Empty<PermissionSummary>();
|
||||
OnPropertyChanged(nameof(ActiveItemsSource));
|
||||
```
|
||||
|
||||
Do NOT change:
|
||||
- Constructor signatures (both full and test constructors remain unchanged)
|
||||
- Existing properties (SiteUrl, IncludeInherited, ScanFolders, etc.)
|
||||
- ExportCsvCommand and ExportHtmlCommand implementations (export updates are in plan 08-04)
|
||||
- OpenSitePickerCommand
|
||||
- _hasLocalSiteOverride / OnGlobalSitesChanged logic
|
||||
</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 has IsSimplifiedMode, IsDetailView, SimplifiedResults, Summaries, and ActiveItemsSource properties. Toggling IsSimplifiedMode rebuilds simplified data from cached Results without re-scanning. Toggling IsDetailView triggers ActiveItemsSource change notification. Existing tests still compile (no constructor changes).</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
- `dotnet build SharepointToolbox/SharepointToolbox.csproj` succeeds with 0 errors
|
||||
- `dotnet test SharepointToolbox.Tests/ --filter PermissionsViewModelTests` passes (no constructor changes)
|
||||
- PermissionsViewModel has IsSimplifiedMode, IsDetailView, SimplifiedResults, Summaries, ActiveItemsSource
|
||||
- Toggling IsSimplifiedMode calls RebuildSimplifiedData + raises ActiveItemsSource changed
|
||||
- RunOperationAsync calls RebuildSimplifiedData when IsSimplifiedMode is true
|
||||
- OnTenantSwitched resets SimplifiedResults and Summaries
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
The ViewModel is the orchestration layer for SIMP-01/02/03. All mode toggles re-render from cached data. The View (08-03) can bind to IsSimplifiedMode, IsDetailView, ActiveItemsSource, and Summaries. Export services (08-04) can access SimplifiedResults and IsSimplifiedMode.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/08-simplified-permissions/08-02-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user