124 lines
6.8 KiB
Markdown
124 lines
6.8 KiB
Markdown
---
|
|
phase: 11
|
|
title: HTML Export Branding + ViewModel Integration
|
|
status: ready-for-planning
|
|
created: 2026-04-08
|
|
---
|
|
|
|
# Phase 11 Context: HTML Export Branding + ViewModel Integration
|
|
|
|
## Decided Areas (from Phase 10 context + STATE.md)
|
|
|
|
These are locked — do not re-litigate during planning or execution.
|
|
|
|
| Decision | Value |
|
|
|---|---|
|
|
| Logo storage format | Base64 strings in JSON (not file paths) |
|
|
| MSP logo location | `BrandingSettings.MspLogo` → `branding.json` via `BrandingRepository` |
|
|
| Client logo location | `TenantProfile.ClientLogo` (per-tenant, in profile JSON) |
|
|
| Logo model | `LogoData { string Base64, string MimeType }` — shared by both MSP and client logos |
|
|
| SVG support | Rejected (XSS risk) — PNG/JPG only |
|
|
| Export service signature change | Optional `ReportBranding? branding = null` parameter on existing `BuildHtml` methods |
|
|
| No new interfaces | No `IHtmlExportService<T>` — keep concrete classes with optional branding param |
|
|
| Report header layout | `display: flex; gap: 16px` — MSP logo left, client logo right |
|
|
| Logo HTML format | `<img src="data:{MimeType};base64,{Base64}">` inline data-URI |
|
|
| No new NuGet packages | All capabilities provided by existing stack |
|
|
|
|
## Phase Goal
|
|
|
|
All five HTML reports display MSP and client logos in a consistent header, and administrators can manage logos from Settings and the profile dialog without touching the View layer.
|
|
|
|
## Success Criteria
|
|
|
|
1. Running any of the five HTML exports (Permissions, Storage, Search, Duplicates, User Access) produces an HTML file whose header contains the MSP logo `<img>` tag when an MSP logo is configured
|
|
2. When a client logo is configured for the active tenant, the same HTML export header contains both the MSP logo and the client logo side by side
|
|
3. When no logo is configured, the HTML export header contains no broken image placeholder and the report renders identically to the pre-branding output
|
|
4. SettingsViewModel exposes browse/clear commands for MSP logo; ProfileManagementViewModel exposes browse/clear commands for client logo — both commands are exercisable without opening any View
|
|
5. Auto-pulling the client logo from the tenant's Entra branding API stores the logo in the tenant profile and falls back silently when no Entra branding is configured
|
|
|
|
## Depends On
|
|
|
|
Phase 10 (completed) — provides `LogoData`, `BrandingSettings`, `BrandingRepository`, `IBrandingService`, `TenantProfile.ClientLogo`
|
|
|
|
## Requirements Mapped
|
|
|
|
- **BRAND-05**: Logos appear in HTML report headers
|
|
- **BRAND-04**: Auto-pull client logo from Entra branding API
|
|
|
|
## Code Context
|
|
|
|
### Phase 10 Infrastructure (already built)
|
|
|
|
| Asset | Path | Role |
|
|
|---|---|---|
|
|
| LogoData record | `Core/Models/LogoData.cs` | `{ string Base64, string MimeType }` |
|
|
| BrandingSettings model | `Core/Models/BrandingSettings.cs` | `{ LogoData? MspLogo }` |
|
|
| TenantProfile model | `Core/Models/TenantProfile.cs` | `{ LogoData? ClientLogo }` (per-tenant) |
|
|
| IBrandingService | `Services/IBrandingService.cs` | `ImportLogoAsync`, `SaveMspLogoAsync`, `ClearMspLogoAsync`, `GetMspLogoAsync` |
|
|
| BrandingService | `Services/BrandingService.cs` | Validates PNG/JPG via magic bytes, auto-compresses >512KB |
|
|
| BrandingRepository | `Infrastructure/Persistence/BrandingRepository.cs` | JSON persistence with SemaphoreSlim + atomic write |
|
|
|
|
### HTML Export Services (5 targets for branding injection)
|
|
|
|
| Service | Path | `BuildHtml` Signature | Header Location |
|
|
|---|---|---|---|
|
|
| HtmlExportService | `Services/Export/HtmlExportService.cs` | `BuildHtml(IReadOnlyList<PermissionEntry>)` | `<h1>SharePoint Permissions Report</h1>` at line 76 |
|
|
| HtmlExportService (simplified) | Same file | `BuildHtml(IReadOnlyList<SimplifiedPermissionEntry>)` (2nd overload) | Similar pattern |
|
|
| SearchHtmlExportService | `Services/Export/SearchHtmlExportService.cs` | `BuildHtml(IReadOnlyList<SearchResult>)` | `<h1>File Search Results</h1>` at line 46 |
|
|
| StorageHtmlExportService | `Services/Export/StorageHtmlExportService.cs` | `BuildHtml(IReadOnlyList<StorageNode>)` | `<h1>SharePoint Storage Metrics</h1>` at line 51 |
|
|
| DuplicatesHtmlExportService | `Services/Export/DuplicatesHtmlExportService.cs` | `BuildHtml(IReadOnlyList<DuplicateGroup>)` | `<h1>Duplicate Detection Report</h1>` at line 55 |
|
|
| UserAccessHtmlExportService | `Services/Export/UserAccessHtmlExportService.cs` | `BuildHtml(IReadOnlyList<UserAccessEntry>)` | `<h1>User Access Audit Report</h1>` at line 91 |
|
|
|
|
### WriteAsync Signatures (7 overloads across 5 services)
|
|
|
|
```csharp
|
|
// HtmlExportService.cs
|
|
WriteAsync(IReadOnlyList<PermissionEntry>, string filePath, CancellationToken)
|
|
WriteAsync(IReadOnlyList<SimplifiedPermissionEntry>, string filePath, CancellationToken)
|
|
|
|
// SearchHtmlExportService.cs
|
|
WriteAsync(IReadOnlyList<SearchResult>, string filePath, CancellationToken)
|
|
|
|
// StorageHtmlExportService.cs
|
|
WriteAsync(IReadOnlyList<StorageNode>, string filePath, CancellationToken)
|
|
WriteAsync(IReadOnlyList<StorageNode>, IReadOnlyList<FileTypeMetric>, string filePath, CancellationToken)
|
|
|
|
// DuplicatesHtmlExportService.cs
|
|
WriteAsync(IReadOnlyList<DuplicateGroup>, string filePath, CancellationToken)
|
|
|
|
// UserAccessHtmlExportService.cs
|
|
WriteAsync(IReadOnlyList<UserAccessEntry>, string filePath, CancellationToken)
|
|
```
|
|
|
|
### ViewModels That Trigger Exports (5 targets)
|
|
|
|
| ViewModel | Path | Export Call Pattern |
|
|
|---|---|---|
|
|
| PermissionsViewModel | `ViewModels/Tabs/PermissionsViewModel.cs` | `_htmlExportService.WriteAsync(Results/SimplifiedResults, ...)` |
|
|
| SearchViewModel | `ViewModels/Tabs/SearchViewModel.cs` | `_htmlExportService.WriteAsync(Results, ...)` |
|
|
| StorageViewModel | `ViewModels/Tabs/StorageViewModel.cs` | `_htmlExportService.WriteAsync(Results, FileTypeMetrics, ...)` |
|
|
| DuplicatesViewModel | `ViewModels/Tabs/DuplicatesViewModel.cs` | `_htmlExportService.WriteAsync(_lastGroups, ...)` |
|
|
| UserAccessAuditViewModel | `ViewModels/Tabs/UserAccessAuditViewModel.cs` | `_htmlExportService.WriteAsync(Results, ...)` |
|
|
|
|
### Logo Management ViewModels (2 targets)
|
|
|
|
| ViewModel | Path | Current State |
|
|
|---|---|---|
|
|
| SettingsViewModel | `ViewModels/Tabs/SettingsViewModel.cs` | Has language + data folder; needs MSP logo browse/clear commands |
|
|
| ProfileManagementViewModel | `ViewModels/ProfileManagementViewModel.cs` | Has CRUD profiles; needs client logo browse/clear/auto-pull commands |
|
|
|
|
### DI Registration
|
|
|
|
`App.xaml.cs` — All export services registered as `Transient`, branding services registered as `Singleton`.
|
|
|
|
### HTML Generation Pattern
|
|
|
|
All 5 HTML exporters use StringBuilder with inline HTML/CSS/JS. No template files. Each builds a self-contained single-file report. The branding header must be injected between `<body>` and the existing `<h1>` tag in each exporter.
|
|
|
|
## Deferred Ideas (out of scope for Phase 11)
|
|
|
|
- Logo preview in Settings UI (Phase 12)
|
|
- Live thumbnail preview after import (Phase 12)
|
|
- "Pull from Entra" button in profile dialog UI (Phase 12)
|
|
- User directory browse mode (Phase 13-14)
|