From afe69bd37f597db46ffdda77507384247b3d5665 Mon Sep 17 00:00:00 2001 From: Dev Date: Thu, 2 Apr 2026 14:13:45 +0200 Subject: [PATCH] feat(02-07): create PermissionsView XAML + code-behind and register DI - Created PermissionsView.xaml with left scan-config panel and right results DataGrid - Created PermissionsView.xaml.cs wiring ViewModel via IServiceProvider, factory for SitePickerDialog - Updated App.xaml.cs: registered IPermissionsService, ISiteListService, CsvExportService, HtmlExportService, PermissionsViewModel, PermissionsView, SitePickerDialog, and Func factory; also registered ISessionManager -> SessionManager - Updated MainWindow.xaml: replaced FeatureTabBase stub with named PermissionsTabItem - Updated MainWindow.xaml.cs: wires PermissionsTabItem.Content from DI-resolved PermissionsView - Added CurrentProfile public accessor, SitesSelectedLabel computed property, and IsMaxDepth toggle property to PermissionsViewModel - Build: 0 errors, 0 warnings. Tests: 60 passed, 3 skipped (live/interactive) --- SharepointToolbox/App.xaml.cs | 15 +++ SharepointToolbox/MainWindow.xaml | 4 +- SharepointToolbox/MainWindow.xaml.cs | 3 + .../ViewModels/Tabs/PermissionsViewModel.cs | 35 +++++ .../Views/Tabs/PermissionsView.xaml | 123 ++++++++++++++++++ .../Views/Tabs/PermissionsView.xaml.cs | 22 ++++ 6 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 SharepointToolbox/Views/Tabs/PermissionsView.xaml create mode 100644 SharepointToolbox/Views/Tabs/PermissionsView.xaml.cs diff --git a/SharepointToolbox/App.xaml.cs b/SharepointToolbox/App.xaml.cs index 69731a6..692bbb8 100644 --- a/SharepointToolbox/App.xaml.cs +++ b/SharepointToolbox/App.xaml.cs @@ -3,10 +3,12 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Serilog; +using SharepointToolbox.Core.Models; using SharepointToolbox.Infrastructure.Auth; using SharepointToolbox.Infrastructure.Logging; using SharepointToolbox.Infrastructure.Persistence; using SharepointToolbox.Services; +using SharepointToolbox.Services.Export; using SharepointToolbox.ViewModels; using SharepointToolbox.ViewModels.Tabs; using SharepointToolbox.Views.Dialogs; @@ -77,6 +79,7 @@ public partial class App : Application services.AddSingleton(_ => new SettingsRepository(Path.Combine(appData, "settings.json"))); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(sp => sp.GetRequiredService()); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -84,6 +87,18 @@ public partial class App : Application services.AddTransient(); services.AddTransient(); services.AddTransient(); + + // Phase 2: Permissions + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient>(sp => + profile => new SitePickerDialog(sp.GetRequiredService(), profile)); + services.AddSingleton(); } } diff --git a/SharepointToolbox/MainWindow.xaml b/SharepointToolbox/MainWindow.xaml index f6a8f2c..7f01def 100644 --- a/SharepointToolbox/MainWindow.xaml +++ b/SharepointToolbox/MainWindow.xaml @@ -41,8 +41,8 @@ - - + diff --git a/SharepointToolbox/MainWindow.xaml.cs b/SharepointToolbox/MainWindow.xaml.cs index 68ae713..6e7cc9b 100644 --- a/SharepointToolbox/MainWindow.xaml.cs +++ b/SharepointToolbox/MainWindow.xaml.cs @@ -20,6 +20,9 @@ public partial class MainWindow : Window // Wire profile management dialog factory viewModel.OpenProfileManagementDialog = () => serviceProvider.GetRequiredService(); + // Replace Permissions tab placeholder with the DI-resolved PermissionsView + PermissionsTabItem.Content = serviceProvider.GetRequiredService(); + // Replace Settings tab placeholder with the DI-resolved SettingsView SettingsTabItem.Content = serviceProvider.GetRequiredService(); diff --git a/SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs b/SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs index 5415b31..7d2bc30 100644 --- a/SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs +++ b/SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs @@ -44,9 +44,27 @@ public partial class PermissionsViewModel : FeatureViewModelBase [ObservableProperty] private int _folderDepth = 1; + /// + /// When true, sets FolderDepth to 999 (scan all levels). + /// + public bool IsMaxDepth + { + get => FolderDepth >= 999; + set + { + if (value) + FolderDepth = 999; + else if (FolderDepth >= 999) + FolderDepth = 1; + OnPropertyChanged(); + } + } + [ObservableProperty] private ObservableCollection _results = new(); + partial void OnFolderDepthChanged(int value) => OnPropertyChanged(nameof(IsMaxDepth)); + // ── Commands ──────────────────────────────────────────────────────────── public IAsyncRelayCommand ExportCsvCommand { get; } @@ -69,6 +87,19 @@ public partial class PermissionsViewModel : FeatureViewModelBase internal TenantProfile? _currentProfile; + /// + /// Public accessor for the current tenant profile — used by View layer dialog factory. + /// + public TenantProfile? CurrentProfile => _currentProfile; + + /// + /// Label shown in the UI: "3 site(s) selected" or empty when none are selected. + /// + public string SitesSelectedLabel => + SelectedSites.Count > 0 + ? string.Format(Localization.TranslationSource.Instance["perm.sites.selected"], SelectedSites.Count) + : string.Empty; + // ── Constructors ──────────────────────────────────────────────────────── /// @@ -93,6 +124,7 @@ public partial class PermissionsViewModel : FeatureViewModelBase ExportCsvCommand = new AsyncRelayCommand(ExportCsvAsync, CanExport); ExportHtmlCommand = new AsyncRelayCommand(ExportHtmlAsync, CanExport); OpenSitePickerCommand = new RelayCommand(ExecuteOpenSitePicker); + SelectedSites.CollectionChanged += (_, _) => OnPropertyChanged(nameof(SitesSelectedLabel)); } /// @@ -115,6 +147,7 @@ public partial class PermissionsViewModel : FeatureViewModelBase ExportCsvCommand = new AsyncRelayCommand(ExportCsvAsync, CanExport); ExportHtmlCommand = new AsyncRelayCommand(ExportHtmlAsync, CanExport); OpenSitePickerCommand = new RelayCommand(ExecuteOpenSitePicker); + SelectedSites.CollectionChanged += (_, _) => OnPropertyChanged(nameof(SitesSelectedLabel)); } // ── FeatureViewModelBase implementation ───────────────────────────────── @@ -184,6 +217,8 @@ public partial class PermissionsViewModel : FeatureViewModelBase Results = new ObservableCollection(); SiteUrl = string.Empty; SelectedSites.Clear(); + OnPropertyChanged(nameof(SitesSelectedLabel)); + OnPropertyChanged(nameof(CurrentProfile)); ExportCsvCommand.NotifyCanExecuteChanged(); ExportHtmlCommand.NotifyCanExecuteChanged(); } diff --git a/SharepointToolbox/Views/Tabs/PermissionsView.xaml b/SharepointToolbox/Views/Tabs/PermissionsView.xaml new file mode 100644 index 0000000..7a8516d --- /dev/null +++ b/SharepointToolbox/Views/Tabs/PermissionsView.xaml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + +