Add report logos and configurable folder scan depth

Report branding (top-left MSP logo, top-right client logo):
- Add MspLogo to AppSettings; client logo already on TenantProfile
- IUserSessionService.CurrentBranding composes MSP + active profile logo
- New reusable LogoUpload component (InputFile -> base64 LogoData, 512KB cap)
- MSP logo upload in Settings; optional client logo in profile create/edit
- Wire ReportBranding into all 6 HTML export pages
- Fix EditProfile dropping ClientLogo on edit

Storage metrics: expose folder scan depth (0-20) in scan options UI,
passed to existing StorageScanOptions.FolderDepth recursion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-02 14:56:49 +02:00
parent 881f3a8bac
commit 5df7b72800
12 changed files with 112 additions and 9 deletions
+10 -2
View File
@@ -21,6 +21,13 @@
<input class="form-input" @bind="_siteUrl" placeholder="@Session.CurrentProfile!.TenantUrl" />
</div>
</div>
<div class="form-row">
<div class="form-group" style="max-width:220px">
<label class="form-label">Folder scan depth</label>
<input class="form-input" type="number" min="0" max="20" @bind="_folderDepth" />
<small class="text-muted">0 = libraries only. 1+ = drill into subfolders that many levels deep.</small>
</div>
</div>
<div class="form-row">
<div class="form-group" style="display:flex;align-items:center;gap:16px;padding-top:20px">
<label><input type="checkbox" @bind="_includeSubsites" /> Include subsites</label>
@@ -81,6 +88,7 @@
@code {
private string _siteUrl = string.Empty;
private bool _includeSubsites, _includeHidden = true, _includeRecycleBin = true;
private int _folderDepth;
private bool _running; private string _status = string.Empty, _error = string.Empty;
private int _current, _total;
private List<StorageNode> _results = new();
@@ -94,7 +102,7 @@
var progress = new Progress<OperationProgress>(p => { _status = p.Message; _current = p.Current; _total = p.Total; InvokeAsync(StateHasChanged); });
try
{
var opts = new StorageScanOptions(IncludeSubsites: _includeSubsites, IncludeHiddenLibraries: _includeHidden, IncludeRecycleBin: _includeRecycleBin);
var opts = new StorageScanOptions(IncludeSubsites: _includeSubsites, FolderDepth: Math.Clamp(_folderDepth, 0, 20), IncludeHiddenLibraries: _includeHidden, IncludeRecycleBin: _includeRecycleBin);
_results = (await Elevation.RunAsync(async c =>
{
var ctx = await SessionMgr.GetOrCreateContextAsync(siteUrl, Session.CurrentProfile!, c);
@@ -116,7 +124,7 @@
}
private async Task ExportHtml()
{
var html = HtmlExport.BuildHtml(_results);
var html = HtmlExport.BuildHtml(_results, Session.CurrentBranding);
await WebExport.DownloadHtmlAsync(html, $"storage_{DateTime.Now:yyyyMMdd_HHmmss}.html");
}
}