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
165 lines
6.3 KiB
Text
165 lines
6.3 KiB
Text
@page
|
|
@model PlanTempusAdmin.Pages.Azure.IndexModel
|
|
@{
|
|
ViewData["Title"] = "Azure Storage";
|
|
}
|
|
|
|
@section Styles {
|
|
<link rel="stylesheet" href="~/css/pages/azure.css" asp-append-version="true" />
|
|
}
|
|
|
|
<div class="page-header">
|
|
<h1 class="page-title">Azure Blob Storage</h1>
|
|
<p class="page-subtitle">@Model.Dashboard.AccountName</p>
|
|
</div>
|
|
|
|
@if (!Model.IsConnected)
|
|
{
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<p class="text-danger">Kan ikke forbinde til Azure Storage</p>
|
|
<p class="text-muted">Tjek at <code>ConnectionStrings:AzureStorage</code> er konfigureret i appsettings.json</p>
|
|
</div>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
var d = Model.Dashboard;
|
|
|
|
<!-- Hero Stats -->
|
|
<div class="status-grid">
|
|
<div class="status-item">
|
|
<div class="status-label">Status</div>
|
|
<div class="status-value success">ONLINE</div>
|
|
<div class="status-detail">@d.AccountName</div>
|
|
</div>
|
|
<div class="status-item">
|
|
<div class="status-label">Containers</div>
|
|
<div class="status-value">@d.TotalContainers</div>
|
|
<div class="status-detail">@d.TotalBlobs blobs total</div>
|
|
</div>
|
|
<div class="status-item">
|
|
<div class="status-label">Total Størrelse</div>
|
|
<div class="status-value">@FormatBytes(d.TotalSize)</div>
|
|
<div class="status-detail">Backup: @FormatBytes(d.BackupTotalSize)</div>
|
|
</div>
|
|
<div class="status-item">
|
|
<div class="status-label">Sidste Upload</div>
|
|
<div class="status-value @(d.LastBackupUpload.HasValue && (DateTimeOffset.Now - d.LastBackupUpload.Value).TotalHours < 24 ? "success" : "warning")">
|
|
@FormatTimeAgo(d.LastBackupUpload)
|
|
</div>
|
|
<div class="status-detail">@d.BackupFileCount backup filer</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="dashboard-grid mt-2">
|
|
<!-- Containers -->
|
|
<div class="card">
|
|
<div class="card-header">Containers</div>
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>Navn</th>
|
|
<th>Blobs</th>
|
|
<th>Størrelse</th>
|
|
<th>Ændret</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (var container in d.Containers)
|
|
{
|
|
<tr>
|
|
<td>
|
|
@if (container.Name.Contains("backup", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
<span class="container-icon">💾</span>
|
|
}
|
|
else
|
|
{
|
|
<span class="container-icon">📦</span>
|
|
}
|
|
<code>@container.Name</code>
|
|
</td>
|
|
<td>@container.BlobCount</td>
|
|
<td>@FormatBytes(container.TotalSize)</td>
|
|
<td>@FormatTimeAgo(container.LastModified)</td>
|
|
<td>
|
|
<a href="/Azure/Container?name=@container.Name" class="btn btn-sm">Åbn</a>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Recent Uploads -->
|
|
<div class="card">
|
|
<div class="card-header">Seneste Uploads</div>
|
|
<div class="card-body compact-list">
|
|
@if (d.RecentBlobs.Count == 0)
|
|
{
|
|
<p class="text-muted">Ingen filer endnu</p>
|
|
}
|
|
@foreach (var blob in d.RecentBlobs)
|
|
{
|
|
<div class="list-item">
|
|
<div class="item-main">
|
|
<span class="file-icon">@GetFileIcon(blob.Name)</span>
|
|
<code class="blob-name" title="@blob.Name">@blob.FileName</code>
|
|
@if (!string.IsNullOrEmpty(blob.AccessTier))
|
|
{
|
|
<span class="badge badge-tier">@blob.AccessTier</span>
|
|
}
|
|
</div>
|
|
<div class="item-meta">
|
|
@FormatBytes(blob.Size) · @FormatTimeAgo(blob.LastModified)
|
|
@if (!string.IsNullOrEmpty(blob.Directory))
|
|
{
|
|
<text>· @blob.Directory</text>
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Last Updated -->
|
|
<div class="last-updated mt-2">
|
|
Opdateret: @DateTime.Now.ToString("HH:mm:ss")
|
|
</div>
|
|
}
|
|
|
|
@functions {
|
|
string FormatBytes(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]}";
|
|
}
|
|
|
|
string FormatTimeAgo(DateTimeOffset? time)
|
|
{
|
|
if (!time.HasValue) return "-";
|
|
var diff = DateTimeOffset.Now - time.Value;
|
|
if (diff.TotalMinutes < 1) return "lige nu";
|
|
if (diff.TotalMinutes < 60) return $"{(int)diff.TotalMinutes}m siden";
|
|
if (diff.TotalHours < 24) return $"{(int)diff.TotalHours}t siden";
|
|
if (diff.TotalDays < 7) return $"{(int)diff.TotalDays}d siden";
|
|
return time.Value.ToString("dd/MM");
|
|
}
|
|
|
|
string GetFileIcon(string name)
|
|
{
|
|
if (name.EndsWith(".tar.gz")) return "📦";
|
|
if (name.EndsWith(".gz") || name.EndsWith(".zip") || name.EndsWith(".7z")) return "📦";
|
|
if (name.EndsWith(".sql")) return "🐘";
|
|
if (name.EndsWith(".bak")) return "💾";
|
|
if (name.EndsWith(".log")) return "📜";
|
|
if (name.EndsWith(".json")) return "📋";
|
|
if (name.EndsWith(".xml")) return "📄";
|
|
return "📄";
|
|
}
|
|
}
|