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>
10 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 | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 08-simplified-permissions | 02 | execute | 2 |
|
|
true |
|
|
Purpose: This is the ViewModel logic for all three SIMP requirements. The View (08-03) binds to these new properties. Output: Updated PermissionsViewModel.cs
<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>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/08-simplified-permissions/08-01-SUMMARY.md From SharepointToolbox/Core/Models/RiskLevel.cs: ```csharp public enum RiskLevel { High, Medium, Low, ReadOnly } ```From SharepointToolbox/Core/Helpers/PermissionLevelMapping.cs:
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:
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:
public record PermissionSummary(string Label, RiskLevel RiskLevel, int Count, int DistinctUsers);
public static class PermissionSummaryBuilder
{
public static IReadOnlyList<PermissionSummary> Build(IEnumerable<SimplifiedPermissionEntry> entries);
}
From SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs:
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)
}
```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
cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5
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).
- `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
<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>
After completion, create `.planning/phases/08-simplified-permissions/08-02-SUMMARY.md`