feat(08-02): add simplified mode properties to PermissionsViewModel

- IsSimplifiedMode toggle switches between raw and simplified labels
- IsDetailView toggle controls individual vs summary row display
- SimplifiedResults and Summaries computed from cached Results
- ActiveItemsSource provides correct collection for DataGrid binding
- Mode toggles rebuild from cache without re-running scan
- OnTenantSwitched resets simplified state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dev
2026-04-07 14:09:57 +02:00
parent 3c70884022
commit e2c94bf6d1

View File

@@ -9,6 +9,7 @@ using Microsoft.Win32;
using SharepointToolbox.Core.Messages; using SharepointToolbox.Core.Messages;
using SharepointToolbox.Core.Models; using SharepointToolbox.Core.Models;
using SharepointToolbox.Services; using SharepointToolbox.Services;
using SharepointToolbox.Core.Helpers;
using SharepointToolbox.Services.Export; using SharepointToolbox.Services.Export;
namespace SharepointToolbox.ViewModels.Tabs; namespace SharepointToolbox.ViewModels.Tabs;
@@ -63,6 +64,51 @@ public partial class PermissionsViewModel : FeatureViewModelBase
[ObservableProperty] [ObservableProperty]
private ObservableCollection<PermissionEntry> _results = new(); private ObservableCollection<PermissionEntry> _results = new();
/// <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;
/// <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;
partial void OnFolderDepthChanged(int value) => OnPropertyChanged(nameof(IsMaxDepth)); partial void OnFolderDepthChanged(int value) => OnPropertyChanged(nameof(IsMaxDepth));
// ── Commands ──────────────────────────────────────────────────────────── // ── Commands ────────────────────────────────────────────────────────────
@@ -167,6 +213,28 @@ public partial class PermissionsViewModel : FeatureViewModelBase
SelectedSites.Add(site); SelectedSites.Add(site);
} }
partial void OnIsSimplifiedModeChanged(bool value)
{
if (value && Results.Count > 0)
RebuildSimplifiedData();
OnPropertyChanged(nameof(ActiveItemsSource));
}
partial void OnIsDetailViewChanged(bool value)
{
OnPropertyChanged(nameof(ActiveItemsSource));
}
/// <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);
}
protected override async Task RunOperationAsync(CancellationToken ct, IProgress<OperationProgress> progress) protected override async Task RunOperationAsync(CancellationToken ct, IProgress<OperationProgress> progress)
{ {
var urls = SelectedSites.Count > 0 var urls = SelectedSites.Count > 0
@@ -213,11 +281,17 @@ public partial class PermissionsViewModel : FeatureViewModelBase
await dispatcher.InvokeAsync(() => await dispatcher.InvokeAsync(() =>
{ {
Results = new ObservableCollection<PermissionEntry>(allEntries); Results = new ObservableCollection<PermissionEntry>(allEntries);
if (IsSimplifiedMode)
RebuildSimplifiedData();
OnPropertyChanged(nameof(ActiveItemsSource));
}); });
} }
else else
{ {
Results = new ObservableCollection<PermissionEntry>(allEntries); Results = new ObservableCollection<PermissionEntry>(allEntries);
if (IsSimplifiedMode)
RebuildSimplifiedData();
OnPropertyChanged(nameof(ActiveItemsSource));
} }
ExportCsvCommand.NotifyCanExecuteChanged(); ExportCsvCommand.NotifyCanExecuteChanged();
@@ -231,6 +305,9 @@ public partial class PermissionsViewModel : FeatureViewModelBase
_currentProfile = profile; _currentProfile = profile;
_hasLocalSiteOverride = false; _hasLocalSiteOverride = false;
Results = new ObservableCollection<PermissionEntry>(); Results = new ObservableCollection<PermissionEntry>();
SimplifiedResults = Array.Empty<SimplifiedPermissionEntry>();
Summaries = Array.Empty<PermissionSummary>();
OnPropertyChanged(nameof(ActiveItemsSource));
SiteUrl = string.Empty; SiteUrl = string.Empty;
SelectedSites.Clear(); SelectedSites.Clear();
OnPropertyChanged(nameof(SitesSelectedLabel)); OnPropertyChanged(nameof(SitesSelectedLabel));