@page "/Stats" @using Butter.Dtos.Stats @inject LoginService LoginService @inject StatsService StatsService Statistics @if (_loading) {
} else if (_stats == null) {
Failed to load statistics. You may not have the required permissions.
} else {

Statistics

@if (_lastUpdated is not null) { Updated @_lastUpdated.Value.ToLocalTime().ToString("HH:mm:ss") }
@* ---- Top-level counts ---- *@
@* ---- Recent activity ---- *@
@* ---- Data completeness indicators ---- *@
@* ---- Duplicate / Orphan / Visibility ---- *@
@* ---- Assets by type + Storage side by side ---- *@
TypeCount @foreach (var kv in _stats.AssetsByType.OrderByDescending(x => x.Value)) { @kv.Key@kv.Value.ToString("N0") } TypeSize @foreach (var kv in _stats.StorageByType.OrderByDescending(x => x.Value)) { @kv.Key@Formatting.FormatBytes(kv.Value) } Total@Formatting.FormatBytes(_stats.TotalStorageBytes)
@* ---- Users by role + File format breakdown ---- *@
RoleCount @foreach (var kv in _stats.UsersByAccessLevel.OrderByDescending(x => x.Value)) { @kv.Key@kv.Value.ToString("N0") } MIME TypeCount @foreach (var fmt in _stats.FileFormatBreakdown.Take(20)) { @fmt.MimeType@fmt.Count.ToString("N0") }
@* ---- Resolution distribution ---- *@
BucketCount @foreach (var bucket in _stats.ResolutionDistribution) { @bucket.Label@bucket.Count.ToString("N0") }
@* ---- Top tags ---- *@
TagAssets @foreach (var tag in _stats.TopTags) { @tag.Name@tag.AssetCount.ToString("N0") } @if (!_stats.TopTags.Any()) { No tags yet. }
@* ---- Monthly growth ---- *@
MonthNew AssetsNew Users @foreach (var m in _stats.MonthlyGrowth) { @(new DateTime(m.Year, m.Month, 1).ToString("yyyy-MM")) @m.NewAssets.ToString("N0") @m.NewUsers.ToString("N0") }
@* ---- Charts ---- *@
Storage by Type
File Formats
Resolution Distribution
Monthly Growth (last 12 months)
} @code { private StatsDto? _stats; private bool _loading = true; private bool _refreshing; private DateTime? _lastUpdated; private static readonly string[] Palette = [ "#0d6efd", "#198754", "#0dcaf0", "#ffc107", "#fd7e14", "#dc3545", "#6f42c1", "#20c997", "#6610f2", "#d63384" ]; private List StorageChartLabels => _stats!.StorageByType.OrderByDescending(x => x.Value).Select(x => x.Key.ToString()).ToList(); private List StorageChartDatasets => [ new() { Label = "Storage", Data = _stats!.StorageByType.OrderByDescending(x => x.Value).Select(x => (double)x.Value).ToList(), BackgroundColor = Palette.Take(_stats!.StorageByType.Count).ToList() } ]; private List FormatChartLabels => _stats!.FileFormatBreakdown.Take(8).Select(x => x.MimeType).ToList(); private List FormatChartDatasets => [ new() { Label = "Assets", Data = _stats!.FileFormatBreakdown.Take(8).Select(x => (double)x.Count).ToList(), BackgroundColor = Palette.Take(_stats!.FileFormatBreakdown.Take(8).Count()).ToList() } ]; private List ResolutionChartLabels => _stats!.ResolutionDistribution.Select(x => x.Label).ToList(); private List ResolutionChartDatasets => [ new() { Label = "Assets", Data = _stats!.ResolutionDistribution.Select(x => (double)x.Count).ToList(), BackgroundColor = Palette.Take(_stats!.ResolutionDistribution.Count).ToList() } ]; private List GrowthChartLabels => _stats!.MonthlyGrowth.Select(m => new DateTime(m.Year, m.Month, 1).ToString("yyyy-MM")).ToList(); private List GrowthChartDatasets => [ new() { Label = "New Assets", Data = _stats!.MonthlyGrowth.Select(m => (double)m.NewAssets).ToList(), BorderColor = "#0d6efd", Fill = false, Tension = 0.2 }, new() { Label = "New Users", Data = _stats!.MonthlyGrowth.Select(m => (double)m.NewUsers).ToList(), BorderColor = "#198754", Fill = false, Tension = 0.2 } ]; protected override async Task OnInitializedAsync() { await LoadStats(); } private async Task Refresh() { if (_refreshing) return; _refreshing = true; StateHasChanged(); await LoadStats(); _refreshing = false; StateHasChanged(); } private async Task LoadStats() { _loading = true; StateHasChanged(); _stats = await StatsService.GetStatsAsync(); _lastUpdated = DateTime.UtcNow; _loading = false; StateHasChanged(); } }