< Summary

Line coverage
100%
Covered lines: 51
Uncovered lines: 0
Coverable lines: 51
Total lines: 242
Line coverage: 100%
Branch coverage
100%
Covered branches: 52
Total branches: 52
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: BuildRenderTree(...)100%44100%
File 2: .ctor()100%11100%
File 2: GetFriendlyServiceName(...)100%1212100%
File 2: GetServiceIcon(...)100%1212100%
File 2: GetStatusSeverity(...)100%66100%
File 2: GetStatusColor(...)100%66100%
File 2: GetOverallSeverity(...)100%66100%
File 2: GetOverallStatusIcon(...)100%66100%

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Client/Pages/Admin/Status.razor

#LineLine coverage
 1@page "/admin/status"
 2@attribute [Authorize]
 3@inject IAdminAuthService AdminAuth
 4@inject IHealthStatusApiService HealthStatusApi
 5@inject NavigationManager Navigation
 6@using Chronicis.Shared.DTOs
 7
 8<PageTitle>System Status - Chronicis</PageTitle>
 9
 1110@if (_authorized == null)
 11{
 12    <MudProgressLinear Indeterminate="true" Color="Color.Primary" />
 13}
 1014else if (_authorized == false)
 15{
 16    <MudAlert Severity="Severity.Error" Class="ma-4">
 17        You do not have permission to access this page.
 18    </MudAlert>
 19}
 20else
 21{
 22    <MudPaper Elevation="2" Class="admin-container pa-6">
 23        <div class="admin-header mb-6">
 24            <div class="d-flex justify-space-between align-center">
 25                <div>
 26                    <MudText Typo="Typo.h4" Class="admin-title">System Status</MudText>
 27                    <MudText Typo="Typo.body2" Class="mud-text-secondary">
 28                        Monitor the health of all system dependencies.
 29                    </MudText>
 30                </div>
 31                <MudButton Variant="Variant.Outlined"
 32                          StartIcon="@Icons.Material.Filled.Refresh"
 33                          OnClick="RefreshStatus"
 34                          Disabled="@_isLoading">
 35                    Refresh
 36                </MudButton>
 37            </div>
 38
 39            @if (_lastChecked.HasValue)
 40            {
 41                <MudText Typo="Typo.caption" Class="mud-text-secondary mt-2">
 42                    Last checked: @_lastChecked.Value.ToString("MMM dd, yyyy 'at' h:mm tt")
 43                </MudText>
 44            }
 45        </div>
 46
 47        <!-- Admin Navigation -->
 48        <div class="mb-4">
 49            <MudButtonGroup Variant="Variant.Outlined" Color="Color.Primary">
 50                <MudButton StartIcon="@Icons.Material.Filled.Build"
 51                          Href="/admin/utilities"
 52                          Variant="@(Navigation.Uri.Contains("/utilities") ? Variant.Filled : Variant.Outlined)">
 53                    Utilities
 54                </MudButton>
 55                <MudButton StartIcon="@Icons.Material.Filled.HealthAndSafety"
 56                          Href="/admin/status"
 57                          Variant="@(Navigation.Uri.Contains("/status") ? Variant.Filled : Variant.Outlined)">
 58                    System Status
 59                </MudButton>
 60            </MudButtonGroup>
 61        </div>
 62
 63        @if (_isLoading)
 64        {
 65            <div class="d-flex justify-center ma-6">
 66                <MudProgressCircular Indeterminate="true" Size="Size.Large" />
 67            </div>
 68        }
 69        else if (_healthStatus == null)
 70        {
 71            <MudAlert Severity="Severity.Error" Class="ma-4">
 72                Unable to fetch system status. The health API may be unavailable.
 73            </MudAlert>
 74        }
 75        else
 76        {
 77            <!-- Overall Status Banner -->
 78            <MudAlert Severity="@GetOverallSeverity(_healthStatus.OverallStatus)"
 79                     Class="mb-4">
 80                <div class="d-flex align-center">
 81                    <MudText Typo="Typo.h6">
 82                        System is @_healthStatus.OverallStatus.ToUpperInvariant()
 83                    </MudText>
 84                </div>
 85            </MudAlert>
 86
 87            <!-- Service Status Grid -->
 88            <MudGrid>
 89                @foreach (var service in _healthStatus.Services)
 90                {
 91                    var displayName = GetFriendlyServiceName(service.ServiceKey);
 92                    var severity = GetStatusSeverity(service.Status);
 93                    var statusColor = GetStatusColor(service.Status);
 94
 95                    <MudItem xs="12" sm="6" md="4">
 96                        <MudCard Class="mud-height-full">
 97                            <MudCardContent>
 98                                <div class="d-flex align-center mb-3">
 99                                    <MudIcon Icon="@GetServiceIcon(service.ServiceKey)"
 100                                            Color="@statusColor"
 101                                            Class="mr-3"
 102                                            Size="Size.Large" />
 103                                    <div class="flex-grow-1">
 104                                        <MudText Typo="Typo.h6">@displayName</MudText>
 105                                        <MudChip T="string"
 106                                                Size="Size.Small"
 107                                                Color="@statusColor"
 108                                                Variant="Variant.Filled"
 109                                                Label="true"
 110                                                Text="@service.Status.ToUpperInvariant()" />
 111                                    </div>
 112                                </div>
 113
 114                                <MudDivider Class="mb-3" />
 115
 116                                <div class="status-details">
 117                                    <MudText Typo="Typo.body2" Class="mb-1">
 118                                        <strong>Response:</strong> @($"{service.ResponseTimeMs:F0}ms")
 119                                    </MudText>
 120
 121                                    @if (!string.IsNullOrEmpty(service.Message))
 122                                    {
 123                                        <MudText Typo="Typo.body2" Class="mb-1">
 124                                            <strong>Details:</strong> @service.Message
 125                                        </MudText>
 126                                    }
 127
 128                                    <MudText Typo="Typo.caption" Class="mud-text-secondary">
 129                                        Checked: @service.CheckedAt.ToString("h:mm:ss tt")
 130                                    </MudText>
 131                                </div>
 132                            </MudCardContent>
 133                        </MudCard>
 134                    </MudItem>
 135                }
 136            </MudGrid>
 137        }
 138    </MudPaper>
 139}

/home/runner/work/chronicis/chronicis/src/Chronicis.Client/Pages/Admin/Status.razor.cs

#LineLine coverage
 1using Chronicis.Shared.DTOs;
 2using MudBlazor;
 3
 4namespace Chronicis.Client.Pages.Admin;
 5
 6public partial class Status
 7{
 8    private bool? _authorized;
 9    private SystemHealthStatusDto? _healthStatus;
 1010    private bool _isLoading = true;
 11    private DateTime? _lastChecked;
 12
 13    protected override async Task OnInitializedAsync()
 14    {
 15        _authorized = await AdminAuth.IsSysAdminAsync();
 16
 17        if (_authorized == true)
 18        {
 19            await LoadHealthStatus();
 20        }
 21    }
 22
 23    private async Task RefreshStatus()
 24    {
 25        await LoadHealthStatus();
 26    }
 27
 28    private async Task LoadHealthStatus()
 29    {
 30        _isLoading = true;
 31        StateHasChanged();
 32
 33        try
 34        {
 35            _healthStatus = await HealthStatusApi.GetSystemHealthAsync();
 36            _lastChecked = DateTime.Now;
 37        }
 38        catch (Exception ex)
 39        {
 40            // Log error but don't throw - we'll show the error state
 41            Console.WriteLine($"Failed to load health status: {ex.Message}");
 42        }
 43        finally
 44        {
 45            _isLoading = false;
 46            StateHasChanged();
 47        }
 48    }
 49
 950    private static string GetFriendlyServiceName(string serviceKey) => serviceKey switch
 951    {
 152        ServiceKeys.Api => "API",
 153        ServiceKeys.Database => "Database",
 154        ServiceKeys.AzureOpenAI => "AI",
 155        ServiceKeys.BlobStorage => "Storage",
 156        ServiceKeys.Auth0 => "Auth",
 157        ServiceKeys.Open5e => "External Data",
 358        _ => serviceKey
 959    };
 60
 961    private static string GetServiceIcon(string serviceKey) => serviceKey switch
 962    {
 163        ServiceKeys.Api => Icons.Material.Filled.Api,
 164        ServiceKeys.Database => Icons.Material.Filled.Storage,
 165        ServiceKeys.AzureOpenAI => Icons.Material.Filled.Psychology,
 166        ServiceKeys.BlobStorage => Icons.Material.Filled.Cloud,
 167        ServiceKeys.Auth0 => Icons.Material.Filled.Security,
 168        ServiceKeys.Open5e => Icons.Material.Filled.Extension,
 369        _ => Icons.Material.Filled.Help
 970    };
 71
 972    private static Severity GetStatusSeverity(string status) => status switch
 973    {
 174        HealthStatus.Healthy => Severity.Success,
 175        HealthStatus.Degraded => Severity.Warning,
 176        HealthStatus.Unhealthy => Severity.Error,
 677        _ => Severity.Info
 978    };
 79
 980    private static Color GetStatusColor(string status) => status switch
 981    {
 182        HealthStatus.Healthy => Color.Success,
 183        HealthStatus.Degraded => Color.Warning,
 184        HealthStatus.Unhealthy => Color.Error,
 685        _ => Color.Info
 986    };
 87
 888    private static Severity GetOverallSeverity(string overallStatus) => overallStatus switch
 889    {
 190        HealthStatus.Healthy => Severity.Success,
 191        HealthStatus.Degraded => Severity.Warning,
 192        HealthStatus.Unhealthy => Severity.Error,
 593        _ => Severity.Info
 894    };
 95
 496    private static string GetOverallStatusIcon(string overallStatus) => overallStatus switch
 497    {
 198        HealthStatus.Healthy => Icons.Material.Filled.CheckCircle,
 199        HealthStatus.Degraded => Icons.Material.Filled.Warning,
 1100        HealthStatus.Unhealthy => Icons.Material.Filled.Error,
 1101        _ => Icons.Material.Filled.Info
 4102    };
 103}