--- phase: 03-storage plan: 07 subsystem: ui tags: [wpf, mvvm, datagrid, ivalueconverter, di, storage, xaml] # Dependency graph requires: - phase: 03-storage plan 03-02 provides: IStorageService/StorageService — storage scan engine - phase: 03-storage plan 03-03 provides: StorageCsvExportService, StorageHtmlExportService - phase: 03-storage plan 03-06 provides: localization keys for Storage tab UI provides: - StorageViewModel: IStorageService orchestration with FlattenNode, export commands, tenant-switching - StorageView.xaml: DataGrid with IndentLevel-based Thickness margin for tree-indent display - StorageView.xaml.cs: code-behind wiring DataContext - IndentConverter, BytesConverter, InverseBoolConverter registered in Application.Resources - RightAlignStyle registered in Application.Resources - Storage tab wired in MainWindow via DI-resolved StorageView affects: [03-08, phase-04-teams] # Tech tracking tech-stack: added: [] patterns: - StorageViewModel uses FeatureViewModelBase + AsyncRelayCommand (same as PermissionsViewModel) - TenantProfile site override via new profile with site URL (ClientContext.Url is read-only) - IValueConverter triple registration in App.xaml: IndentConverter/BytesConverter/InverseBoolConverter - FlattenNode recursive helper assigns IndentLevel pre-Dispatcher.InvokeAsync key-files: created: - SharepointToolbox/ViewModels/Tabs/StorageViewModel.cs - SharepointToolbox/Views/Tabs/StorageView.xaml - SharepointToolbox/Views/Tabs/StorageView.xaml.cs - SharepointToolbox/Views/Converters/IndentConverter.cs modified: - SharepointToolbox/App.xaml - SharepointToolbox/App.xaml.cs - SharepointToolbox/MainWindow.xaml - SharepointToolbox/MainWindow.xaml.cs - SharepointToolbox/Services/Export/SearchCsvExportService.cs - SharepointToolbox/Services/Export/SearchHtmlExportService.cs key-decisions: - "ClientContext.Url is read-only in CSOM — must create new TenantProfile with site URL for GetOrCreateContextAsync (same approach as PermissionsViewModel)" - "IndentConverter/BytesConverter/InverseBoolConverter registered in App.xaml Application.Resources — accessible to all views without per-UserControl declaration" - "StorageView XAML omits local UserControl.Resources converter declarations — uses Application-level StaticResource references instead" patterns-established: - "Site-scoped operations create new TenantProfile{TenantUrl=siteUrl, ClientId/Name from current profile}" - "FlattenNode pre-assigns IndentLevel before Dispatcher.InvokeAsync to avoid cross-thread collection mutation" requirements-completed: [STOR-01, STOR-02, STOR-03, STOR-04, STOR-05] # Metrics duration: 4min completed: 2026-04-02 --- # Phase 03 Plan 07: StorageViewModel + StorageView XAML + DI Wiring Summary **StorageViewModel orchestrating IStorageService via FeatureViewModelBase + StorageView DataGrid with IndentConverter-based tree indentation, fully wired through DI in MainWindow** ## Performance - **Duration:** ~4 min - **Started:** 2026-04-02T13:35:02Z - **Completed:** 2026-04-02T13:39:00Z - **Tasks:** 2 - **Files modified:** 10 ## Accomplishments - StorageViewModel created with RunOperationAsync → IStorageService.CollectStorageAsync, FlattenNode tree-flattening, Dispatcher.InvokeAsync-safe ObservableCollection update - StorageView.xaml DataGrid with IndentLevel-driven Thickness margin, BytesConverter for human-readable sizes, all scan/export controls bound to ViewModel - IndentConverter, BytesConverter, InverseBoolConverter, and RightAlignStyle registered in App.xaml Application.Resources - Storage tab live in MainWindow via DI-resolved StorageView (same pattern as Permissions tab) ## Task Commits Each task was committed atomically: 1. **Task 1: Create StorageViewModel** - `e174a18` (feat) 2. **Task 2: Create StorageView XAML + code-behind, update DI and MainWindow wiring** - `e08452d` (feat) ## Files Created/Modified - `SharepointToolbox/ViewModels/Tabs/StorageViewModel.cs` - Storage tab ViewModel (IStorageService orchestration, export commands, tenant-switching) - `SharepointToolbox/Views/Tabs/StorageView.xaml` - Storage tab XAML (DataGrid + scan controls + export buttons) - `SharepointToolbox/Views/Tabs/StorageView.xaml.cs` - Code-behind wiring DataContext to StorageViewModel - `SharepointToolbox/Views/Converters/IndentConverter.cs` - IndentConverter, BytesConverter, InverseBoolConverter in one file - `SharepointToolbox/App.xaml` - Registered three converters and RightAlignStyle in Application.Resources - `SharepointToolbox/App.xaml.cs` - Phase 3 Storage DI registrations (IStorageService, exports, VM, View) - `SharepointToolbox/MainWindow.xaml` - Added x:Name=StorageTabItem to Storage TabItem - `SharepointToolbox/MainWindow.xaml.cs` - Wired StorageTabItem.Content from DI - `SharepointToolbox/Services/Export/SearchCsvExportService.cs` - Added missing System.IO using - `SharepointToolbox/Services/Export/SearchHtmlExportService.cs` - Added missing System.IO using ## Decisions Made - `ClientContext.Url` is read-only in CSOM — the site URL override is done by creating a new `TenantProfile` with `TenantUrl = SiteUrl` (same ClientId/Name from current profile), passed to `GetOrCreateContextAsync`. - All three converters (IndentConverter, BytesConverter, InverseBoolConverter) registered at Application scope in App.xaml rather than per-view, avoiding duplicate resource key definitions. - `StorageView.xaml` omits local `UserControl.Resources` declarations for converters — references Application-level `StaticResource` instead, keeping the XAML clean. ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 1 - Bug] Fixed ClientContext.Url read-only assignment in StorageViewModel.RunOperationAsync** - **Found during:** Task 1 (StorageViewModel creation) - **Issue:** Plan included `ctx.Url = SiteUrl.TrimEnd('/')` but `ClientRuntimeContext.Url` is a read-only property in CSOM - **Fix:** Created a new `TenantProfile{TenantUrl=siteUrl, ClientId, Name}` and passed it to `GetOrCreateContextAsync` — the context is keyed by URL so it gets or creates the right session - **Files modified:** `SharepointToolbox/ViewModels/Tabs/StorageViewModel.cs` - **Verification:** Build succeeded with 0 errors - **Committed in:** `e174a18` (Task 1 commit) **2. [Rule 3 - Blocking] Added missing System.IO using to SearchCsvExportService and SearchHtmlExportService** - **Found during:** Task 1 build verification - **Issue:** Both Search export services used `File.WriteAllTextAsync` without `using System.IO;` — same established project convention (WPF project does not include System.IO in implicit usings) - **Fix:** Added `using System.IO;` to both files - **Files modified:** `SharepointToolbox/Services/Export/SearchCsvExportService.cs`, `SharepointToolbox/Services/Export/SearchHtmlExportService.cs` - **Verification:** Build succeeded with 0 errors; 82 tests pass - **Committed in:** `e174a18` (Task 1 commit) --- **Total deviations:** 2 auto-fixed (1 bug, 1 blocking) **Impact on plan:** Both auto-fixes necessary for correctness. No scope creep. ## Issues Encountered None beyond the two auto-fixed deviations above. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - StorageView is live and functional — users can enter site URL, configure scan options, run scan, and export results - Plans 03-03 (StorageCsvExportService) and 03-06 (localization keys) are prerequisites and were already completed - Ready for Wave 4: Plan 03-08 (SearchViewModel + DuplicatesViewModel + Views + visual checkpoint) - All 82 tests passing, 10 expected skips (CSOM live-connection tests) --- *Phase: 03-storage* *Completed: 2026-04-02*