docs(16-02): complete consolidated HTML export plan
- SUMMARY.md: BuildConsolidatedHtml with expandable location sub-lists, by-site view suppression, ViewModel wiring - STATE.md: updated position, decisions, session - ROADMAP.md: phase 16 marked Complete (2/2 plans with summaries)
This commit is contained in:
125
.planning/phases/16-report-consolidation-toggle/16-02-SUMMARY.md
Normal file
125
.planning/phases/16-report-consolidation-toggle/16-02-SUMMARY.md
Normal file
@@ -0,0 +1,125 @@
|
||||
---
|
||||
phase: 16-report-consolidation-toggle
|
||||
plan: "02"
|
||||
subsystem: export
|
||||
tags: [html-export, consolidation, expandable-rows, toggleGroup, tdd]
|
||||
dependency-graph:
|
||||
requires: [16-01, 15-01, 15-02]
|
||||
provides: [Consolidated HTML export path, expandable location sub-lists, by-site view suppression]
|
||||
affects: [UserAccessHtmlExportService, UserAccessAuditViewModel]
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [early-return branch, optional parameter default, TDD red-green, private method extraction]
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- SharepointToolbox/Services/Export/UserAccessHtmlExportService.cs
|
||||
- SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs
|
||||
- SharepointToolbox.Tests/Services/Export/UserAccessHtmlExportServiceTests.cs
|
||||
decisions:
|
||||
- "BuildConsolidatedHtml is a private method called via early-return in BuildHtml — existing non-consolidated code path is completely untouched"
|
||||
- "Separate locIdx counter for location group IDs (loc0, loc1...) is distinct from grpIdx for user group IDs (ugrp0, ugrp1...) — avoids ID collision Pitfall 2"
|
||||
- "Existing branding test call site updated to use named parameter branding: because mergePermissions was inserted before branding in the signature"
|
||||
metrics:
|
||||
duration: ~10min
|
||||
completed: 2026-04-09
|
||||
tasks: 2
|
||||
files-modified: 3
|
||||
---
|
||||
|
||||
# Phase 16 Plan 02: Consolidated HTML Rendering Path Summary
|
||||
|
||||
Consolidated HTML export with expandable [N sites] badges using toggleGroup() JS, by-site view suppression when mergePermissions=true, and ViewModel wiring for the HTML export call site.
|
||||
|
||||
## Tasks Completed
|
||||
|
||||
| Task | Name | Commit | Files |
|
||||
|------|------|--------|-------|
|
||||
| 1 (RED) | Add failing tests for RPT-03-b through RPT-03-e | 3d95d2a | UserAccessHtmlExportServiceTests.cs |
|
||||
| 1 (GREEN) | Implement consolidated HTML rendering path and wire ViewModel | 0ebe707 | UserAccessHtmlExportService.cs, UserAccessAuditViewModel.cs, UserAccessHtmlExportServiceTests.cs |
|
||||
| 2 | Full solution build and test suite verification | — | (no source changes — verification only) |
|
||||
|
||||
## What Was Built
|
||||
|
||||
### BuildHtml Signature Change
|
||||
`BuildHtml` now accepts `bool mergePermissions = false` as the second parameter (before `branding`):
|
||||
```csharp
|
||||
public string BuildHtml(IReadOnlyList<UserAccessEntry> entries, bool mergePermissions = false, ReportBranding? branding = null)
|
||||
```
|
||||
The early-return branch:
|
||||
```csharp
|
||||
if (mergePermissions)
|
||||
{
|
||||
var consolidated = PermissionConsolidator.Consolidate(entries);
|
||||
return BuildConsolidatedHtml(consolidated, entries, branding);
|
||||
}
|
||||
```
|
||||
The entire existing code path below this branch is completely untouched.
|
||||
|
||||
### WriteAsync Signature Change
|
||||
`WriteAsync` now accepts `bool mergePermissions = false` after `ct` and passes it through to `BuildHtml`:
|
||||
```csharp
|
||||
public async Task WriteAsync(IReadOnlyList<UserAccessEntry> entries, string filePath, CancellationToken ct, bool mergePermissions = false, ReportBranding? branding = null)
|
||||
```
|
||||
|
||||
### BuildConsolidatedHtml Private Method
|
||||
Produces a consolidated HTML report:
|
||||
- Same HTML shell (DOCTYPE, head, CSS, stats cards, user summary cards)
|
||||
- Single by-user table with columns: User, Permission Level, Access Type, Granted Through, Sites
|
||||
- Group headers per UserLogin with `ugrp{n}` IDs
|
||||
- Sites column behavior:
|
||||
- 1 location: plain text site title (no badge, no sub-rows)
|
||||
- 2+ locations: `<span class="badge" onclick="toggleGroup('loc{n}')">N sites</span>` with hidden sub-rows (`data-group="loc{n}" style="display:none"`) containing linked site titles
|
||||
- By-site view (view-site div) is completely omitted
|
||||
- btn-site is completely omitted — only By User button rendered
|
||||
- Separate `locIdx` counter for location groups, distinct from `grpIdx` for user groups
|
||||
|
||||
### ViewModel Wiring
|
||||
`UserAccessAuditViewModel.ExportHtmlAsync` now passes `MergePermissions` to `WriteAsync`:
|
||||
```csharp
|
||||
await _htmlExportService.WriteAsync(Results, dialog.FileName, CancellationToken.None, MergePermissions, branding);
|
||||
```
|
||||
|
||||
### Tests (TDD)
|
||||
4 new test methods added:
|
||||
- `BuildHtml_mergePermissionsFalse_identical_to_default` — RPT-03-b: byte-identical output to default call
|
||||
- `BuildHtml_mergePermissionsTrue_contains_sites_column` — RPT-03-c: "Sites" column header present
|
||||
- `BuildHtml_mergePermissionsTrue_multiLocation_has_badge_and_subrows` — RPT-03-d: onclick toggleGroup + data-group=loc pattern
|
||||
- `BuildHtml_mergePermissionsTrue_omits_bysite_view` — RPT-03-e: no btn-site, no view-site
|
||||
|
||||
All 12 UserAccessHtmlExportServiceTests pass. Full suite: 302 passed, 26 skipped.
|
||||
|
||||
## Decisions Made
|
||||
|
||||
1. **Early-return + private method** — Same pattern as Plan 01's consolidated CSV path. `BuildConsolidatedHtml` is extracted as a private method to keep `BuildHtml` clean. The branch guarantees the existing code path is never reached when `mergePermissions=true`.
|
||||
|
||||
2. **Separate locIdx counter** — RESEARCH.md Pitfall 2 explicitly warned about ID collision between user group headers and location sub-rows. Used distinct `int grpIdx` (for `ugrp{n}`) and `int locIdx` (for `loc{n}`) to prevent any overlap.
|
||||
|
||||
3. **Named parameter fix for branding test** — The existing `BuildHtml_WithBranding_ContainsLogoImg` test called `BuildHtml(entries, MakeBranding(...))` positionally. Inserting `mergePermissions` before `branding` broke that call — fixed by adding `branding:` named argument. This is a Rule 1 auto-fix (broken test).
|
||||
|
||||
## Verification Results
|
||||
|
||||
- `dotnet build` — 0 errors, 0 warnings
|
||||
- `dotnet test --filter "FullyQualifiedName~UserAccessHtmlExportServiceTests"` — 12/12 passed
|
||||
- `dotnet test` full suite — 302 passed, 26 skipped, 0 failed (no regressions)
|
||||
- Test count increased by 4 from Plan 02 (RPT-03-b through RPT-03-e) + 2 previously flaky tests now stable
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 1 - Bug] Fixed branding test call site broken by parameter insertion**
|
||||
- **Found during:** Task 1 (GREEN phase — first test run)
|
||||
- **Issue:** Existing `BuildHtml_WithBranding_ContainsLogoImg` test passed `MakeBranding(...)` as positional argument 2, which collided with the newly inserted `bool mergePermissions` parameter at position 2
|
||||
- **Fix:** Added named `branding:` argument: `svc.BuildHtml(new[] { DefaultEntry }, branding: MakeBranding(msp: true))`
|
||||
- **Files modified:** SharepointToolbox.Tests/Services/Export/UserAccessHtmlExportServiceTests.cs
|
||||
- **Commit:** 0ebe707
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
Files exist:
|
||||
- SharepointToolbox/Services/Export/UserAccessHtmlExportService.cs — contains `mergePermissions`, `BuildConsolidatedHtml`, `PermissionConsolidator.Consolidate`
|
||||
- SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs — contains `MergePermissions` in WriteAsync call
|
||||
- SharepointToolbox.Tests/Services/Export/UserAccessHtmlExportServiceTests.cs — contains 4 new consolidation test methods
|
||||
|
||||
Commits exist: 3d95d2a (RED), 0ebe707 (GREEN)
|
||||
Reference in New Issue
Block a user