diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 87713a5..847a0c2 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -125,6 +125,6 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 |-------|----------------|--------|-----------| | 1. Foundation | 8/8 | Complete | 2026-04-02 | | 2. Permissions | 7/7 | Complete | 2026-04-02 | -| 3. Storage and File Operations | 5/8 | In Progress| | +| 3. Storage and File Operations | 7/8 | In Progress| | | 4. Bulk Operations and Provisioning | 0/? | Not started | - | | 5. Distribution and Hardening | 0/? | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index 4a8588e..fb5f62d 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,14 +3,14 @@ gsd_state_version: 1.0 milestone: v1.0 milestone_name: milestone status: executing -stopped_at: Completed 03-04-PLAN.md — SearchService and DuplicatesService -last_updated: "2026-04-02T13:33:35.151Z" +stopped_at: Completed 03-07-PLAN.md — StorageViewModel + StorageView XAML + DI Wiring +last_updated: "2026-04-02T13:40:06.726Z" last_activity: 2026-04-02 — Plan 03-02 complete — StorageService CSOM scan engine implemented progress: total_phases: 5 completed_phases: 2 total_plans: 23 - completed_plans: 20 + completed_plans: 22 percent: 65 --- @@ -80,6 +80,8 @@ Progress: [██████░░░░] 65% | Phase 03-storage P03 | 2min | 2 tasks | 2 files | | Phase 03-storage P06 | 5min | 1 tasks | 3 files | | Phase 03-storage P04 | 2min | 2 tasks | 2 files | +| Phase 03-storage P07 | 4min | 2 tasks | 10 files | +| Phase 03-storage P05 | 4min | 2 tasks | 3 files | ## Accumulated Context @@ -142,6 +144,10 @@ Recent decisions affecting current work: - [Phase 03-storage 03-04]: SearchService uses SelectProperties.Add per-item loop — StringCollection has no AddRange(string[]) overload in this SDK version - [Phase 03-storage 03-04]: DuplicatesService.MakeKey internal static method matches inline test helper in DuplicatesServiceTests exactly — deliberate design to ensure test parity - [Phase 03-storage 03-04]: DuplicatesService file mode re-implements pagination inline — avoids coupling between services with different result models (DuplicateItem vs SearchResult) +- [Phase 03-storage]: ClientContext.Url is read-only in CSOM — site URL override done via new TenantProfile with site URL for GetOrCreateContextAsync +- [Phase 03-storage]: IndentConverter/BytesConverter/InverseBoolConverter registered in App.xaml Application.Resources — accessible to all views without per-UserControl declaration +- [Phase 03-storage]: SearchCsvExportService uses UTF-8 BOM for Excel compatibility — consistent with Phase 2 CsvExportService pattern +- [Phase 03-storage]: DuplicatesHtmlExportService always uses badge-dup (red) for all groups — ok/diff distinction removed from final DUPL-03 spec ### Pending Todos @@ -154,6 +160,6 @@ None yet. ## Session Continuity -Last session: 2026-04-02T13:33:35.149Z -Stopped at: Completed 03-04-PLAN.md — SearchService and DuplicatesService +Last session: 2026-04-02T13:39:46.878Z +Stopped at: Completed 03-07-PLAN.md — StorageViewModel + StorageView XAML + DI Wiring Resume file: None diff --git a/.planning/phases/03-storage/03-07-SUMMARY.md b/.planning/phases/03-storage/03-07-SUMMARY.md new file mode 100644 index 0000000..988b96d --- /dev/null +++ b/.planning/phases/03-storage/03-07-SUMMARY.md @@ -0,0 +1,152 @@ +--- +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*