PlanTempusAdmin/Pages/Index.cshtml
Janus C. H. Knudsen 08f8150064 Refactors page styles into separate CSS files
Extracts inline styles from Razor pages into modular CSS files
Adds new CSS files for components and specific page styles
Improves code organization and maintainability by separating styling concerns

Updates layout to include new CSS files and optional style sections
2026-02-03 15:55:44 +01:00

265 lines
10 KiB
Text

@page
@model PlanTempusAdmin.Pages.IndexModel
@{
ViewData["Title"] = "Dashboard";
}
@section Styles {
<link rel="stylesheet" href="~/css/pages/dashboard.css" asp-append-version="true" />
}
<div class="page-header">
<h1 class="page-title">Dashboard</h1>
<p class="page-subtitle">PlanTempus SaaS Infrastructure Status</p>
</div>
<!-- Top Status Grid -->
<div class="status-grid">
<div class="status-item">
<div class="status-label">Caddy Server</div>
<div class="status-value @(Model.CaddyRunning ? "success" : "danger")">
@(Model.CaddyRunning ? "ONLINE" : "OFFLINE")
</div>
<div class="status-detail">@Model.HostCount hosts</div>
</div>
<div class="status-item">
<div class="status-label">Forgejo</div>
<div class="status-value @(Model.ForgejoConnected ? "success" : "danger")">
@if (Model.ForgejoConnected)
{
@if (Model.ForgejoDashboard.RunningNow > 0)
{
<span class="pulse">●</span> @Model.ForgejoDashboard.RunningNow <text> CI</text>
}
else
{
<text>ONLINE</text>
}
}
else
{
<text>OFFLINE</text>
}
</div>
<div class="status-detail">@Model.ForgejoDashboard.TotalRepos repos</div>
</div>
<div class="status-item">
<div class="status-label">Backup</div>
<div class="status-value @(Model.BackupDbConnected ? (Model.LastBackupOk ? "success" : "warning") : "danger")">
@if (Model.BackupDbConnected)
{
@(Model.LastBackupAge ?? "INGEN")
}
else
{
<text>OFFLINE</text>
}
</div>
<div class="status-detail">@Model.BackupSummary.SuccessfulBackups OK / @Model.BackupSummary.FailedBackups fejl</div>
</div>
<div class="status-item">
<div class="status-label">Azure Storage</div>
<div class="status-value @(Model.AzureConnected ? "success" : "danger")">
@(Model.AzureConnected ? "ONLINE" : "OFFLINE")
</div>
<div class="status-detail">@FormatSize(Model.AzureDashboard.TotalSize)</div>
</div>
</div>
<!-- Cards Grid -->
<div class="dashboard-cards mt-2">
<!-- Forgejo Card -->
<div class="card">
<div class="card-header">Forgejo Git</div>
<div class="card-body">
@if (Model.ForgejoConnected)
{
<div class="mini-stats">
<div class="mini-stat">
<div class="mini-stat-value">@Model.ForgejoDashboard.TotalRepos</div>
<div class="mini-stat-label">Repos</div>
</div>
<div class="mini-stat">
<div class="mini-stat-value">@Model.ForgejoDashboard.TotalOpenIssues</div>
<div class="mini-stat-label">Issues</div>
</div>
<div class="mini-stat">
<div class="mini-stat-value">@Model.ForgejoDashboard.TotalOpenPRs</div>
<div class="mini-stat-label">PRs</div>
</div>
<div class="mini-stat">
<div class="mini-stat-value">@FormatSize(Model.ForgejoDashboard.TotalSize * 1024)</div>
<div class="mini-stat-label">Størrelse</div>
</div>
</div>
@if (Model.ForgejoDashboard.RunningRuns.Count > 0)
{
<div class="running-list mt-1">
@foreach (var run in Model.ForgejoDashboard.RunningRuns.Take(3))
{
<div class="running-item">
<span class="pulse">●</span>
<code>@run.FullRepoName</code>
<span class="text-muted">@run.WorkflowId</span>
</div>
}
</div>
}
<a href="/Forgejo" class="btn btn-primary mt-1">Se detaljer</a>
}
else
{
<p class="text-danger">Forgejo database ikke tilgængelig</p>
}
</div>
</div>
<!-- Caddy Card -->
<div class="card">
<div class="card-header">Caddy Reverse Proxy</div>
<div class="card-body">
@if (Model.CaddyRunning)
{
<p>Server kører og håndterer <strong>@Model.HostCount</strong> host(s).</p>
<a href="/Caddy" class="btn btn-primary mt-1">Se detaljer</a>
}
else
{
<p class="text-danger">Caddy server er ikke tilgængelig.</p>
}
</div>
</div>
<!-- Backup Card -->
<div class="card">
<div class="card-header">Backup Status</div>
<div class="card-body">
@if (Model.BackupDbConnected)
{
<div class="mini-stats">
<div class="mini-stat">
<div class="mini-stat-value success">@Model.BackupSummary.SuccessfulBackups</div>
<div class="mini-stat-label">Success</div>
</div>
<div class="mini-stat">
<div class="mini-stat-value @(Model.BackupSummary.FailedBackups > 0 ? "danger" : "")">@Model.BackupSummary.FailedBackups</div>
<div class="mini-stat-label">Fejlet</div>
</div>
<div class="mini-stat">
<div class="mini-stat-value">@FormatSize(Model.BackupSummary.TotalSizeBytes)</div>
<div class="mini-stat-label">Total</div>
</div>
</div>
<a href="/Backup" class="btn btn-primary mt-1">Se detaljer</a>
}
else
{
<p class="text-danger">Backup database er ikke tilgængelig.</p>
}
</div>
</div>
<!-- CI/CD Card -->
<div class="card">
<div class="card-header">CI/CD Actions</div>
<div class="card-body">
@if (Model.ForgejoConnected)
{
<div class="mini-stats">
<div class="mini-stat">
<div class="mini-stat-value success">@Model.ForgejoDashboard.SuccessfulRuns</div>
<div class="mini-stat-label">Success</div>
</div>
<div class="mini-stat">
<div class="mini-stat-value @(Model.ForgejoDashboard.FailedRunsCount > 0 ? "danger" : "")">@Model.ForgejoDashboard.FailedRunsCount</div>
<div class="mini-stat-label">Fejlet</div>
</div>
<div class="mini-stat">
<div class="mini-stat-value">@Model.ForgejoDashboard.RunsThisWeek</div>
<div class="mini-stat-label">Denne uge</div>
</div>
</div>
@if (Model.ForgejoDashboard.FailedRuns.Count > 0)
{
<div class="failed-list mt-1">
<div class="failed-header">Seneste fejl:</div>
@foreach (var run in Model.ForgejoDashboard.FailedRuns.Take(2))
{
<div class="failed-item">
<span class="badge badge-danger">FEJL</span>
<code>@run.FullRepoName</code>
</div>
}
</div>
}
<a href="/Forgejo/Actions" class="btn btn-primary mt-1">Se detaljer</a>
}
else
{
<p class="text-muted">Ikke tilgængelig</p>
}
</div>
</div>
<!-- Azure Storage Card -->
<div class="card">
<div class="card-header">Azure Blob Storage</div>
<div class="card-body">
@if (Model.AzureConnected)
{
<div class="mini-stats">
<div class="mini-stat">
<div class="mini-stat-value">@Model.AzureDashboard.TotalContainers</div>
<div class="mini-stat-label">Containers</div>
</div>
<div class="mini-stat">
<div class="mini-stat-value">@Model.AzureDashboard.TotalBlobs</div>
<div class="mini-stat-label">Blobs</div>
</div>
<div class="mini-stat">
<div class="mini-stat-value">@FormatSize(Model.AzureDashboard.TotalSize)</div>
<div class="mini-stat-label">Størrelse</div>
</div>
<div class="mini-stat">
<div class="mini-stat-value">@Model.AzureDashboard.BackupFileCount</div>
<div class="mini-stat-label">Backups</div>
</div>
</div>
@if (Model.AzureDashboard.RecentBlobs.Count > 0)
{
<div class="running-list mt-1">
@foreach (var blob in Model.AzureDashboard.RecentBlobs.Take(2))
{
<div class="running-item">
<span>📄</span>
<code>@blob.FileName</code>
<span class="text-muted">@FormatSize(blob.Size)</span>
</div>
}
</div>
}
<a href="/Azure" class="btn btn-primary mt-1">Se detaljer</a>
}
else
{
<p class="text-danger">Azure Storage ikke tilgængelig</p>
}
</div>
</div>
</div>
@functions {
string FormatSize(long bytes)
{
if (bytes == 0) return "0 B";
var sizes = new[] { "B", "KB", "MB", "GB", "TB" };
var i = (int)Math.Floor(Math.Log(bytes) / Math.Log(1024));
return $"{Math.Round(bytes / Math.Pow(1024, i), 1)} {sizes[i]}";
}
}