--- phase: 17-group-expansion-html-reports plan: 02 type: execute wave: 2 depends_on: ["17-01"] files_modified: - SharepointToolbox/Services/Export/HtmlExportService.cs - SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs - SharepointToolbox.Tests/Services/Export/HtmlExportServiceTests.cs autonomous: true requirements: [RPT-01, RPT-02] must_haves: truths: - "SharePoint group pills in the HTML report are clickable and expand to show group members" - "When group members are resolved, clicking the pill reveals member names inline" - "When group resolution fails, the pill expands to show 'members unavailable' label" - "When no groupMembers dict is passed, HTML output is identical to pre-Phase 17 output" - "toggleGroup() JS function exists in HtmlExportService inline JS" - "PermissionsViewModel calls ISharePointGroupResolver before HTML export and passes results to BuildHtml" artifacts: - path: "SharepointToolbox/Services/Export/HtmlExportService.cs" provides: "Expandable group pill rendering + toggleGroup JS" contains: "toggleGroup" - path: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs" provides: "Group resolution orchestration before export" contains: "_groupResolver" - path: "SharepointToolbox.Tests/Services/Export/HtmlExportServiceTests.cs" provides: "Tests for group pill expansion and backward compatibility" contains: "grpmem" key_links: - from: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs" to: "ISharePointGroupResolver" via: "constructor injection + call in ExportHtmlAsync" pattern: "_groupResolver.ResolveGroupsAsync" - from: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs" to: "HtmlExportService.BuildHtml" via: "passing groupMembers dict" pattern: "groupMembers" - from: "SharepointToolbox/Services/Export/HtmlExportService.cs" to: "toggleGroup JS" via: "inline script block" pattern: "function toggleGroup" --- Wire group member expansion into HtmlExportService rendering and PermissionsViewModel export flow. Purpose: This is the user-visible feature — SharePoint group pills become clickable in HTML reports, expanding to show resolved members or a "members unavailable" fallback. Output: Modified HtmlExportService (both overloads), modified PermissionsViewModel, new HTML export tests. @C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md @C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/17-group-expansion-html-reports/17-RESEARCH.md @.planning/phases/17-group-expansion-html-reports/17-01-SUMMARY.md From SharepointToolbox/Core/Models/ResolvedMember.cs: ```csharp public record ResolvedMember(string DisplayName, string Login); ``` From SharepointToolbox/Services/ISharePointGroupResolver.cs: ```csharp public interface ISharePointGroupResolver { Task>> ResolveGroupsAsync( ClientContext ctx, string clientId, IReadOnlyList groupNames, CancellationToken ct); } ``` From SharepointToolbox/Services/Export/HtmlExportService.cs: ```csharp public class HtmlExportService { // Overload 1 — standard PermissionEntry public string BuildHtml(IReadOnlyList entries, ReportBranding? branding = null) public async Task WriteAsync(IReadOnlyList entries, string filePath, CancellationToken ct, ReportBranding? branding = null) // Overload 2 — simplified public string BuildHtml(IReadOnlyList entries, ReportBranding? branding = null) public async Task WriteAsync(IReadOnlyList entries, string filePath, CancellationToken ct, ReportBranding? branding = null) } ``` From SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs: ```csharp public partial class PermissionsViewModel : FeatureViewModelBase { private readonly HtmlExportService? _htmlExportService; private readonly ISessionManager _sessionManager; private readonly IBrandingService? _brandingService; private TenantProfile? _currentProfile; public PermissionsViewModel( IPermissionsService permissionsService, ISiteListService siteListService, ISessionManager sessionManager, CsvExportService csvExportService, HtmlExportService htmlExportService, IBrandingService brandingService, ILogger logger) // Test constructor (no export services) internal PermissionsViewModel( IPermissionsService permissionsService, ISiteListService siteListService, ISessionManager sessionManager, ILogger logger, IBrandingService? brandingService = null) private async Task ExportHtmlAsync() { // Currently calls: _htmlExportService.WriteAsync(entries, path, ct, branding) } } ``` From SharepointToolbox/Services/Export/UserAccessHtmlExportService.cs (toggleGroup JS to copy): ```javascript function toggleGroup(id) { var rows = document.querySelectorAll('tr[data-group="' + id + '"]'); rows.forEach(function(r) { r.style.display = r.style.display === 'none' ? '' : 'none'; }); } ``` Task 1: Extend HtmlExportService with groupMembers parameter and expandable group pills SharepointToolbox/Services/Export/HtmlExportService.cs, SharepointToolbox.Tests/Services/Export/HtmlExportServiceTests.cs - RPT-01-a: BuildHtml with no groupMembers (null/omitted) produces output identical to pre-Phase 17 - RPT-01-b: BuildHtml with groupMembers containing a group name renders clickable pill with onclick="toggleGroup('grpmem0')" and class "group-expandable" - RPT-01-c: BuildHtml with resolved members renders hidden sub-row (data-group="grpmem0", display:none) containing member display names - RPT-01-d: BuildHtml with empty member list (resolution failed) renders sub-row with "members unavailable" italic label - RPT-01-e: BuildHtml output contains "function toggleGroup" in inline JS - RPT-01-f: Simplified BuildHtml overload also accepts groupMembers and renders expandable pills identically 1. Add `groupMembers` optional parameter to BOTH `BuildHtml` overloads and BOTH `WriteAsync` methods: ```csharp public string BuildHtml(IReadOnlyList entries, ReportBranding? branding = null, IReadOnlyDictionary>? groupMembers = null) public async Task WriteAsync(IReadOnlyList entries, string filePath, CancellationToken ct, ReportBranding? branding = null, IReadOnlyDictionary>? groupMembers = null) ``` Same for the `SimplifiedPermissionEntry` overloads. `WriteAsync` passes `groupMembers` through to `BuildHtml`. 2. Add CSS for `.group-expandable` in the inline `