---
phase: 11-html-export-branding
plan: 03
type: execute
wave: 3
depends_on: ["11-02"]
files_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
  # Note: App.xaml.cs does NOT need changes — DI container auto-resolves IBrandingService for ViewModel constructors
autonomous: true
requirements:
  - BRAND-05

must_haves:
  truths:
    - "Each of the 5 export ViewModels injects IBrandingService and assembles ReportBranding before calling WriteAsync"
    - "ReportBranding is assembled from IBrandingService.GetMspLogoAsync() for MSP logo and _currentProfile.ClientLogo for client logo"
    - "The branding ReportBranding is passed as the last parameter to WriteAsync"
    - "DI container provides IBrandingService to all 5 export ViewModels"
  artifacts:
    - path: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs"
      provides: "Permissions export with branding assembly"
      contains: "IBrandingService"
    - path: "SharepointToolbox/ViewModels/Tabs/SearchViewModel.cs"
      provides: "Search export with branding assembly"
      contains: "IBrandingService"
    - path: "SharepointToolbox/ViewModels/Tabs/StorageViewModel.cs"
      provides: "Storage export with branding assembly"
      contains: "IBrandingService"
    - path: "SharepointToolbox/ViewModels/Tabs/DuplicatesViewModel.cs"
      provides: "Duplicates export with branding assembly"
      contains: "IBrandingService"
    - path: "SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs"
      provides: "User access export with branding assembly"
      contains: "IBrandingService"
  key_links:
    - from: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs"
      to: "SharepointToolbox/Services/IBrandingService.cs"
      via: "constructor injection"
      pattern: "IBrandingService _brandingService"
    - from: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs"
      to: "SharepointToolbox/Services/Export/HtmlExportService.cs"
      via: "WriteAsync with branding"
      pattern: "WriteAsync.*branding"
---

<objective>
Wire IBrandingService into all 5 export ViewModels so each ExportHtmlAsync method assembles a ReportBranding from the MSP logo and the active tenant's client logo, then passes it to WriteAsync.

Purpose: Connects the branding infrastructure (Plan 01) and export service changes (Plan 02) to the user-facing export commands. After this plan, HTML exports include branding logos when configured.

Output: All 5 export ViewModels inject IBrandingService, assemble ReportBranding in ExportHtmlAsync, and pass it to WriteAsync.
</objective>

<execution_context>
@C:/Users/dev/.claude/get-shit-done/workflows/execute-plan.md
@C:/Users/dev/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/11-html-export-branding/11-CONTEXT.md
@.planning/phases/11-html-export-branding/11-RESEARCH.md
@.planning/phases/11-html-export-branding/11-02-SUMMARY.md

<interfaces>
<!-- From Plan 11-01 -->
From SharepointToolbox/Core/Models/ReportBranding.cs:
```csharp
public record ReportBranding(LogoData? MspLogo, LogoData? ClientLogo);
```

<!-- From Phase 10 -->
From SharepointToolbox/Services/IBrandingService.cs:
```csharp
public interface IBrandingService
{
    Task<LogoData> ImportLogoAsync(string filePath);
    Task SaveMspLogoAsync(LogoData logo);
    Task ClearMspLogoAsync();
    Task<LogoData?> GetMspLogoAsync();
}
```

<!-- Current ViewModel patterns (all 5 follow this same shape) -->
DuplicatesViewModel constructor pattern:
```csharp
public DuplicatesViewModel(
    IDuplicatesService duplicatesService,
    ISessionManager sessionManager,
    DuplicatesHtmlExportService htmlExportService,
    ILogger<FeatureViewModelBase> logger) : base(logger)
```

Each ViewModel has:
- `private TenantProfile? _currentProfile;` field set via OnTenantSwitched
- `ExportHtmlAsync()` method calling `_htmlExportService.WriteAsync(..., CancellationToken.None)`

PermissionsViewModel has two constructors: full (DI) and test (internal, omits export services).
UserAccessAuditViewModel also has two constructors.
StorageViewModel also has two constructors (test constructor at line 151).
The other 2 ViewModels (Search, Duplicates) have a single constructor each.

DI registrations in App.xaml.cs:
```csharp
services.AddSingleton<IBrandingService, BrandingService>();
```
IBrandingService is already registered — no new DI registration needed for the service itself.
But each ViewModel registration must now resolve IBrandingService in addition to existing deps.
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Inject IBrandingService into all 5 export ViewModels and assemble branding in ExportHtmlAsync</name>
  <files>
    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
  </files>
  <action>
    Apply the same pattern to all 5 ViewModels:

    **For each ViewModel:**

    1. Add `using SharepointToolbox.Core.Models;` if not already present (needed for `ReportBranding`).

    2. Add field: `private readonly IBrandingService _brandingService;`

    3. Modify the DI constructor to accept `IBrandingService brandingService` parameter and assign `_brandingService = brandingService;`.

    4. For ViewModels with a test constructor (PermissionsViewModel, UserAccessAuditViewModel, StorageViewModel): add `IBrandingService? brandingService = null` as the last parameter, assign `_brandingService = brandingService!;`. Using `null!` is acceptable because test constructors are only used in tests where branding is not exercised. Alternatively, create a no-op implementation — but `null!` matches existing pattern where `_htmlExportService = null` is already used in test constructors. **Verify that existing test files for all 3 ViewModels still compile after the constructor changes.**

    5. Modify `ExportHtmlAsync()` — add branding assembly BEFORE the WriteAsync call:
       ```csharp
       // Assemble branding
       var mspLogo = await _brandingService.GetMspLogoAsync();
       var clientLogo = _currentProfile?.ClientLogo;
       var branding = new ReportBranding(mspLogo, clientLogo);
       ```
       Then pass `branding` as the last argument to each `WriteAsync` call.

    **Specific details per ViewModel:**

    **PermissionsViewModel** (2 WriteAsync calls in ExportHtmlAsync):
    ```csharp
    // Before:
    await _htmlExportService.WriteAsync(SimplifiedResults.ToList(), dialog.FileName, CancellationToken.None);
    // After:
    await _htmlExportService.WriteAsync(SimplifiedResults.ToList(), dialog.FileName, CancellationToken.None, branding);
    ```
    Same for the non-simplified path.

    **SearchViewModel** (1 WriteAsync call):
    ```csharp
    await _htmlExportService.WriteAsync(Results, dialog.FileName, CancellationToken.None, branding);
    ```

    **StorageViewModel** (1 WriteAsync call — the one with FileTypeMetrics):
    ```csharp
    await _htmlExportService.WriteAsync(Results, FileTypeMetrics, dialog.FileName, CancellationToken.None, branding);
    ```

    **DuplicatesViewModel** (1 WriteAsync call):
    ```csharp
    await _htmlExportService.WriteAsync(_lastGroups, dialog.FileName, CancellationToken.None, branding);
    ```

    **UserAccessAuditViewModel** (1 WriteAsync call):
    ```csharp
    await _htmlExportService.WriteAsync(Results, dialog.FileName, CancellationToken.None, branding);
    ```

    **Guard clause:** Add a null check on `_brandingService` before the branding assembly to be safe (in case the test constructor was used). If `_brandingService is null`, set `branding = null` (which means no branding header — graceful degradation):
    ```csharp
    ReportBranding? branding = null;
    if (_brandingService is not null)
    {
        var mspLogo = await _brandingService.GetMspLogoAsync();
        var clientLogo = _currentProfile?.ClientLogo;
        branding = new ReportBranding(mspLogo, clientLogo);
    }
    ```
  </action>
  <verify>
    <automated>dotnet build --no-restore -warnaserror && dotnet test SharepointToolbox.Tests --no-build -q</automated>
  </verify>
  <done>All 5 export ViewModels inject IBrandingService, assemble ReportBranding from MSP logo + active profile's ClientLogo, and pass it to WriteAsync. Build and all tests pass. Test constructors gracefully handle null IBrandingService.</done>
</task>

</tasks>

<verification>
```bash
dotnet build --no-restore -warnaserror
dotnet test SharepointToolbox.Tests --no-build -q
```
Both commands must pass. Full test suite must pass — existing ViewModel tests must not break from the constructor changes.
</verification>

<success_criteria>
- All 5 export ViewModels have IBrandingService injected via constructor
- ExportHtmlAsync assembles ReportBranding from GetMspLogoAsync + _currentProfile.ClientLogo
- ReportBranding is passed to WriteAsync as the last parameter
- Test constructors handle null IBrandingService gracefully (branding = null fallback)
- All existing ViewModel and export tests pass without modification
- Build succeeds with zero warnings
</success_criteria>

<output>
After completion, create `.planning/phases/11-html-export-branding/11-03-SUMMARY.md`
</output>
