Files
Sharepoint-Toolbox/.planning/phases/16-report-consolidation-toggle/16-02-SUMMARY.md
Dev 1ff99f0bb7 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)
2026-04-09 12:40:00 +02:00

6.7 KiB

phase, plan, subsystem, tags, dependency-graph, tech-stack, key-files, decisions, metrics
phase plan subsystem tags dependency-graph tech-stack key-files decisions metrics
16-report-consolidation-toggle 02 export
html-export
consolidation
expandable-rows
toggleGroup
tdd
requires provides affects
16-01
15-01
15-02
Consolidated HTML export path
expandable location sub-lists
by-site view suppression
UserAccessHtmlExportService
UserAccessAuditViewModel
added patterns
early-return branch
optional parameter default
TDD red-green
private method extraction
created modified
SharepointToolbox/Services/Export/UserAccessHtmlExportService.cs
SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs
SharepointToolbox.Tests/Services/Export/UserAccessHtmlExportServiceTests.cs
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
duration completed tasks files-modified
~10min 2026-04-09 2 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):

public string BuildHtml(IReadOnlyList<UserAccessEntry> entries, bool mergePermissions = false, ReportBranding? branding = null)

The early-return branch:

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:

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:

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)