diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 847a0c2..7299dd2 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -22,7 +22,7 @@ Decimal phases appear between their surrounding integers in numeric order. - [x] **Phase 1: Foundation** - WPF shell, multi-tenant auth, DI, async patterns, error handling, logging, localization, JSON persistence (completed 2026-04-02) - [x] **Phase 2: Permissions** - Permissions scan (single and multi-site), CSV and HTML report export -- [ ] **Phase 3: Storage and File Operations** - Storage metrics, file search, and duplicate detection +- [x] **Phase 3: Storage and File Operations** - Storage metrics, file search, and duplicate detection (completed 2026-04-02) - [ ] **Phase 4: Bulk Operations and Provisioning** - Bulk member/site/transfer operations, site templates, folder structure provisioning - [ ] **Phase 5: Distribution and Hardening** - Self-contained EXE packaging, end-to-end validation, FR locale completeness @@ -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 | 7/8 | In Progress| | +| 3. Storage and File Operations | 8/8 | Complete | 2026-04-02 | | 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 b8c29c5..07e3dd4 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-05-PLAN.md — Search and Duplicate Export Services -last_updated: "2026-04-02T13:40:11.479Z" +stopped_at: Completed 03-08-PLAN.md — SearchViewModel + DuplicatesViewModel + Views + DI wiring (visual checkpoint pending) +last_updated: "2026-04-02T13:46:30.502Z" last_activity: 2026-04-02 — Plan 03-02 complete — StorageService CSOM scan engine implemented progress: total_phases: 5 - completed_phases: 2 + completed_phases: 3 total_plans: 23 - completed_plans: 22 + completed_plans: 23 percent: 65 --- @@ -82,6 +82,7 @@ Progress: [██████░░░░] 65% | 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 | +| Phase 03 P08 | 4min | 3 tasks | 9 files | ## Accumulated Context @@ -148,6 +149,8 @@ Recent decisions affecting current work: - [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 +- [Phase 03]: SearchViewModel and DuplicatesViewModel use TenantProfile site URL override pattern — ctx.Url is read-only in CSOM (established pattern from StorageViewModel) +- [Phase 03]: DuplicateRow flat DTO wraps DuplicateItem with GroupName and GroupSize for DataGrid display ### Pending Todos @@ -160,6 +163,6 @@ None yet. ## Session Continuity -Last session: 2026-04-02T13:40:11.476Z -Stopped at: Completed 03-05-PLAN.md — Search and Duplicate Export Services +Last session: 2026-04-02T13:46:30.499Z +Stopped at: Completed 03-08-PLAN.md — SearchViewModel + DuplicatesViewModel + Views + DI wiring (visual checkpoint pending) Resume file: None diff --git a/.planning/phases/03-storage/03-08-SUMMARY.md b/.planning/phases/03-storage/03-08-SUMMARY.md new file mode 100644 index 0000000..7640cd4 --- /dev/null +++ b/.planning/phases/03-storage/03-08-SUMMARY.md @@ -0,0 +1,81 @@ +--- +phase: 03 +plan: 08 +subsystem: ui-viewmodels +tags: [wpf, viewmodel, search, duplicates, di, xaml] +dependency_graph: + requires: [03-05, 03-06, 03-07] + provides: [SearchViewModel, DuplicatesViewModel, SearchView, DuplicatesView, Phase3-DI] + affects: [App.xaml.cs, MainWindow.xaml, MainWindow.xaml.cs] +tech_stack: + added: [] + patterns: [FeatureViewModelBase, AsyncRelayCommand, TenantProfile-site-override, DI-tab-wiring] +key_files: + created: + - SharepointToolbox/ViewModels/Tabs/SearchViewModel.cs + - SharepointToolbox/Views/Tabs/SearchView.xaml + - SharepointToolbox/Views/Tabs/SearchView.xaml.cs + - SharepointToolbox/ViewModels/Tabs/DuplicatesViewModel.cs + - SharepointToolbox/Views/Tabs/DuplicatesView.xaml + - SharepointToolbox/Views/Tabs/DuplicatesView.xaml.cs + modified: + - SharepointToolbox/App.xaml.cs + - SharepointToolbox/MainWindow.xaml + - SharepointToolbox/MainWindow.xaml.cs +decisions: + - SearchViewModel and DuplicatesViewModel use TenantProfile site URL override pattern — ctx.Url is read-only in CSOM (established pattern from StorageViewModel) + - DuplicateRow flat DTO wraps DuplicateItem with GroupName and GroupSize for DataGrid display +metrics: + duration: 4min + completed_date: "2026-04-02" + tasks: 3 + files: 9 +--- + +# Phase 3 Plan 08: SearchViewModel + DuplicatesViewModel + Views + DI Wiring Summary + +**One-liner:** SearchViewModel and DuplicatesViewModel with full XAML views wired into MainWindow via DI, completing Phase 3 Storage feature tabs. + +## Tasks Completed + +| # | Name | Commit | Files | +|---|------|--------|-------| +| 1a | SearchViewModel + SearchView | 7e6d39a | SearchViewModel.cs, SearchView.xaml, SearchView.xaml.cs | +| 1b | DuplicatesViewModel + DuplicatesView | 0984a36 | DuplicatesViewModel.cs, DuplicatesView.xaml, DuplicatesView.xaml.cs | +| 2 | DI registration + MainWindow wiring | 1f2a49d | App.xaml.cs, MainWindow.xaml, MainWindow.xaml.cs | + +## What Was Built + +**SearchViewModel** (`SearchViewModel.cs`): Full filter state (extensions, regex, 4 date range checkboxes, createdBy, modifiedBy, library, maxResults), `RunOperationAsync` that calls `ISearchService.SearchFilesAsync`, `ExportCsvCommand` + `ExportHtmlCommand` with CanExport guard, `OnTenantSwitched` clears results. + +**SearchView.xaml**: Left filter panel (260px ScrollViewer) with GroupBox for filters, Run Search + Cancel buttons, Export CSV/HTML group, status TextBlock. Right: full-width DataGrid with 8 columns (name, ext, created, author, modified, modifiedBy, size, path) using `BytesConverter` and `RightAlignStyle`. + +**DuplicatesViewModel** (`DuplicatesViewModel.cs`): Mode (Files/Folders), 5 criteria checkboxes, IncludeSubsites, Library, `RunOperationAsync` that calls `IDuplicatesService.ScanDuplicatesAsync`, flattens `DuplicateGroup.Items` to flat `DuplicateRow` list for DataGrid, `ExportHtmlCommand`. + +**DuplicatesView.xaml**: Left options panel (240px) with type RadioButtons, criteria checkboxes, library TextBox, IncludeSubsites checkbox, Run Scan + Cancel + Export HTML buttons. Right: DataGrid with group, copies, name, library, size, created, modified, path columns. + +**DI + Wiring**: App.xaml.cs registers all Phase 3 Search and Duplicates services and views. MainWindow.xaml replaces FeatureTabBase stubs with named TabItems. MainWindow.xaml.cs wires content from DI. + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] Fixed ctx.Url read-only error in SearchViewModel** +- **Found during:** Task 1a verification build +- **Issue:** Plan code used `ctx.Url = SiteUrl.TrimEnd('/')` — `ClientRuntimeContext.Url` is read-only in CSOM (CS0200) +- **Fix:** Replaced with `new TenantProfile { TenantUrl = SiteUrl.TrimEnd('/'), ClientId = ..., Name = ... }` and passed to `GetOrCreateContextAsync` — identical to StorageViewModel pattern documented in STATE.md +- **Files modified:** SearchViewModel.cs +- **Commit:** 7e6d39a (fix applied in same commit) + +**2. [Rule 1 - Bug] Pre-emptively fixed ctx.Url in DuplicatesViewModel** +- **Found during:** Task 1b (same issue pattern as Task 1a) +- **Issue:** Plan code also used `ctx.Url =` for DuplicatesViewModel +- **Fix:** Same TenantProfile override pattern applied before writing the file +- **Files modified:** DuplicatesViewModel.cs +- **Commit:** 0984a36 + +## Pre-existing Test Failure (Out of Scope) + +`FeatureViewModelBaseTests.CancelCommand_DuringOperation_SetsStatusMessageToCancelled` fails because test asserts `.Contains("cancel")` (case-insensitive) but the app returns French string "Opération annulée". This failure predates this plan (confirmed via git stash test). Out of scope — logged to deferred items. + +## Self-Check: PASSED