From 5d3fdee9da4ad29cda8dcdc9bd1e039ee2c83570 Mon Sep 17 00:00:00 2001 From: Dev Date: Wed, 8 Apr 2026 14:51:56 +0200 Subject: [PATCH] docs(11-03): complete ViewModel branding wiring plan - Create 11-03-SUMMARY.md: IBrandingService wired into all 5 export ViewModels - Update STATE.md: decisions, session record, progress - Update ROADMAP.md: Phase 11 marked complete (4/4 plans, all summaries present) --- .planning/ROADMAP.md | 4 +- .planning/STATE.md | 15 ++- .../11-html-export-branding/11-03-SUMMARY.md | 113 ++++++++++++++++++ 3 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 .planning/phases/11-html-export-branding/11-03-SUMMARY.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index edc39cd..8b336a0 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -32,7 +32,7 @@ ### v2.2 Report Branding & User Directory (Phases 10-14) - [x] **Phase 10: Branding Data Foundation** — Models, repository, and services for logo storage and user directory enumeration (completed 2026-04-08) -- [ ] **Phase 11: HTML Export Branding + ViewModel Integration** — Inject logos into all 5 HTML report types; wire branding into export-triggering ViewModels and logo management commands +- [x] **Phase 11: HTML Export Branding + ViewModel Integration** — Inject logos into all 5 HTML report types; wire branding into export-triggering ViewModels and logo management commands (completed 2026-04-08) - [ ] **Phase 12: Branding UI Views** — Settings and profile dialog logo sections with live preview; auto-pull client logo from Entra branding API - [ ] **Phase 13: User Directory ViewModel** — Browse mode state, paginated directory load, member/guest filter, and department/job title columns - [ ] **Phase 14: User Directory View** — Toggle panel in UserAccessAuditView, user selection to trigger existing audit pipeline @@ -111,7 +111,7 @@ Plans: | 1-5 | v1.0 | 36/36 | Shipped | 2026-04-07 | | 6-9 | v1.1 | 25/25 | Shipped | 2026-04-08 | | 10. Branding Data Foundation | v2.2 | 3/3 | Complete | 2026-04-08 | -| 11. HTML Export Branding + ViewModel Integration | 3/4 | In Progress| | — | +| 11. HTML Export Branding + ViewModel Integration | 4/4 | Complete | 2026-04-08 | — | | 12. Branding UI Views | v2.2 | 0/? | Not started | — | | 13. User Directory ViewModel | v2.2 | 0/? | Not started | — | | 14. User Directory View | v2.2 | 0/? | Not started | — | diff --git a/.planning/STATE.md b/.planning/STATE.md index 94d13d9..51a9571 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,14 +3,14 @@ gsd_state_version: 1.0 milestone: v2.2 milestone_name: Report Branding & User Directory status: completed -stopped_at: Completed 11-02-PLAN.md — HTML export branding injection -last_updated: "2026-04-08T12:46:42.149Z" +stopped_at: Completed 11-03-PLAN.md — ViewModel branding wiring +last_updated: "2026-04-08T12:51:43.354Z" last_activity: 2026-04-08 — Phase 11 planning completed progress: total_phases: 5 - completed_phases: 1 + completed_phases: 2 total_plans: 7 - completed_plans: 6 + completed_plans: 7 --- # Project State @@ -62,6 +62,9 @@ Decisions are logged in PROJECT.md Key Decisions table. - [Phase 11-html-export-branding]: InternalsVisibleTo added via MSBuild AssemblyAttribute ItemGroup in csproj - [Phase 11-html-export-branding]: branding parameter placed AFTER CancellationToken ct in WriteAsync — existing positional callers unaffected - [Phase 11-html-export-branding]: MakeBranding helper added locally to each test class — test files stay self-contained +- [Phase 11]: Test constructors on 3 ViewModels received optional IBrandingService? brandingService = null as last parameter to preserve all existing test call sites +- [Phase 11]: Guard clause (if _brandingService is not null) used for graceful degradation — branding = null fallback preserves backward compat +- [Phase 11]: No App.xaml.cs changes needed for ViewModel branding injection — IBrandingService already registered as singleton, ViewModel registrations auto-resolve ### Pending Todos @@ -76,7 +79,7 @@ None. ## Session Continuity -Last session: 2026-04-08T12:46:37.894Z -Stopped at: Completed 11-02-PLAN.md — HTML export branding injection +Last session: 2026-04-08T12:51:43.351Z +Stopped at: Completed 11-03-PLAN.md — ViewModel branding wiring Resume file: None Next step: `/gsd:execute-phase 11` diff --git a/.planning/phases/11-html-export-branding/11-03-SUMMARY.md b/.planning/phases/11-html-export-branding/11-03-SUMMARY.md new file mode 100644 index 0000000..e14b76e --- /dev/null +++ b/.planning/phases/11-html-export-branding/11-03-SUMMARY.md @@ -0,0 +1,113 @@ +--- +phase: 11-html-export-branding +plan: 03 +subsystem: viewmodels +tags: [html-export, branding, csharp, viewmodels, dotnet] + +# Dependency graph +requires: + - phase: 11-01 + provides: ReportBranding record + - phase: 11-02 + provides: Optional ReportBranding? branding parameter on all 5 export service WriteAsync methods + - phase: 10 + provides: IBrandingService registered as singleton in DI; IBrandingService.GetMspLogoAsync() +provides: + - PermissionsViewModel with IBrandingService injection and branding assembly in ExportHtmlAsync + - SearchViewModel with IBrandingService injection and branding assembly in ExportHtmlAsync + - StorageViewModel with IBrandingService injection and branding assembly in ExportHtmlAsync + - DuplicatesViewModel with IBrandingService injection and branding assembly in ExportHtmlAsync + - UserAccessAuditViewModel with IBrandingService injection and branding assembly in ExportHtmlAsync +affects: + - HTML export output (branding header injected when MSP or client logo is configured) + +# Tech tracking +tech-stack: + added: [] + patterns: + - "IBrandingService injected via DI constructor; optional IBrandingService? in test constructors with null default" + - "Guard clause pattern: branding = null when _brandingService is null (graceful degradation in tests)" + - "ReportBranding assembled from GetMspLogoAsync() + _currentProfile?.ClientLogo before each WriteAsync call" + +key-files: + created: [] + modified: + - SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs + - SharepointToolbox/ViewModels/Tabs/SearchViewModel.cs + - SharepointToolbox/ViewModels/Tabs/StorageViewModel.cs + - SharepointToolbox/ViewModels/Tabs/DuplicatesViewModel.cs + - SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs + +key-decisions: + - "Test constructors (PermissionsViewModel, StorageViewModel, UserAccessAuditViewModel) use optional IBrandingService? brandingService = null as last parameter — preserves all existing test call sites without modification" + - "DuplicatesViewModel and SearchViewModel have single constructors only — IBrandingService added as required DI parameter" + - "No App.xaml.cs changes needed — ViewModels registered as AddTransient() with auto-resolution; IBrandingService already registered as singleton in Phase 10" + - "Guard clause uses 'if (_brandingService is not null)' pattern — branding = null fallback means export services render without header (backward compatible)" + +# Metrics +duration: 3min +completed: 2026-04-08 +--- + +# Phase 11 Plan 03: ViewModel Branding Wiring Summary + +**IBrandingService injected into all 5 export ViewModels; ReportBranding assembled from MSP logo + active tenant ClientLogo and passed to WriteAsync in each ExportHtmlAsync method** + +## Performance + +- **Duration:** ~3 min +- **Started:** 2026-04-08T12:47:55Z +- **Completed:** 2026-04-08T12:51:00Z +- **Tasks:** 1 +- **Files modified:** 5 + +## Accomplishments + +- Added `private readonly IBrandingService? _brandingService;` field to PermissionsViewModel, StorageViewModel, UserAccessAuditViewModel (nullable for test constructors) +- Added `private readonly IBrandingService _brandingService;` field to SearchViewModel and DuplicatesViewModel (non-nullable, single constructor) +- Modified DI constructors on all 5 ViewModels to accept `IBrandingService brandingService` parameter +- Modified test constructors on PermissionsViewModel, StorageViewModel, UserAccessAuditViewModel to accept optional `IBrandingService? brandingService = null` as last parameter — all existing test call sites compile unchanged +- Added branding assembly block with guard clause in ExportHtmlAsync for all 5 ViewModels +- Passed `branding` as last argument to WriteAsync in all ExportHtmlAsync methods (2 calls in PermissionsViewModel, 1 each in the other 4) +- No App.xaml.cs changes required — DI auto-resolves IBrandingService for all ViewModel registrations + +## Task Commits + +1. **Task 1: Inject IBrandingService into all 5 export ViewModels** - `816fb5e` (feat) + +## Files Created/Modified + +- `SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs` - IBrandingService field + DI ctor param + optional test ctor param + branding in ExportHtmlAsync (2 WriteAsync calls) +- `SharepointToolbox/ViewModels/Tabs/SearchViewModel.cs` - IBrandingService field + DI ctor param + branding in ExportHtmlAsync +- `SharepointToolbox/ViewModels/Tabs/StorageViewModel.cs` - IBrandingService field + DI ctor param + optional test ctor param + branding in ExportHtmlAsync +- `SharepointToolbox/ViewModels/Tabs/DuplicatesViewModel.cs` - IBrandingService field + DI ctor param + branding in ExportHtmlAsync +- `SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs` - IBrandingService field + DI ctor param + optional test ctor param + branding in ExportHtmlAsync + +## Decisions Made + +- Test constructors on the 3 ViewModels that had them (PermissionsViewModel, StorageViewModel, UserAccessAuditViewModel) received `IBrandingService? brandingService = null` as last optional parameter — this preserves all existing test instantiation call sites without any modification +- Guard clause `if (_brandingService is not null)` chosen over `null!` assignment — cleaner null-safety contract, makes graceful degradation explicit +- No new App.xaml.cs registrations needed — IBrandingService was already registered as singleton in Phase 10, and ViewModel registrations use constructor auto-resolution + +## Deviations from Plan + +None - plan executed exactly as written. + +## Issues Encountered + +A spurious test failure appeared during the stash/unstash verification step (`StorageViewModelChartTests.After_setting_metrics_BarChartSeries_has_one_ColumnSeries_with_matching_values`). This was a stale test binary issue, not a real failure — the test passed on both fresh runs before and after my changes. After proper rebuild, all 254 tests pass. + +## User Setup Required + +None. + +## Next Phase Readiness + +- All 5 export ViewModels now assemble `ReportBranding` from `IBrandingService.GetMspLogoAsync()` and `_currentProfile.ClientLogo` and pass it to WriteAsync +- When MSP and/or client logos are configured, HTML exports will include the branding header automatically +- Phase 11 is now functionally complete (Plans 01-03 done; 11-04 was SettingsViewModel which prior context indicates was already done) +- Build: 0 warnings, 0 errors; test suite: 254 passed / 0 failed / 26 skipped (skips are pre-existing integration tests) + +--- +*Phase: 11-html-export-branding* +*Completed: 2026-04-08*