Files
2026-02-23 14:02:44 +01:00

111 lines
5.1 KiB
Plaintext

@model CalendarSync.Controllers.DashboardViewModel
@{
ViewData["Title"] = "Sync Dashboard";
}
<div class="container-fluid mt-4">
<div class="row mb-4">
<div class="col">
<h1 class="display-4 fw-bold text-primary">Sync Transparency Dashboard</h1>
<p class="lead">Real-time visibility into M365 Webhooks and Delta Syncs.</p>
</div>
</div>
<div class="row">
<!-- User Configs Card -->
<div class="col-lg-5 mb-4">
<div class="card shadow-sm border-0 h-100">
<div class="card-header bg-dark text-white fw-bold">User Configurations</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Email</th>
<th>Expiration</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@foreach (var config in Model.Configs)
{
<tr>
<td>@config.Email</td>
<td>@config.Expiration?.ToString("g")</td>
<td>
<span class="badge @(config.Expiration > DateTimeOffset.Now ? "bg-success" : "bg-danger")">
@(config.Expiration > DateTimeOffset.Now ? "Active" : "Expired")
</span>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Webhook Logs Card -->
<div class="col-lg-7 mb-4">
<div class="card shadow-sm border-0 h-100">
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
<span class="fw-bold">Webhook Transaction Logs</span>
<span id="sync-status" class="badge bg-light text-primary">SignalR Connected</span>
</div>
<div class="card-body p-0">
<div class="table-responsive" style="max-height: 600px; overflow-y: auto;">
<table class="table table-sm table-hover mb-0" id="logs-table">
<thead class="table-light">
<tr>
<th>Received At</th>
<th>Type</th>
<th>Payload (First 50 chars)</th>
</tr>
</thead>
<tbody id="logs-body">
@foreach (var log in Model.Logs)
{
<tr>
<td>@log.ReceivedAt.ToString("HH:mm:ss")</td>
<td><span class="badge @(log.EndpointType == "Listen" ? "bg-info" : "bg-warning") text-dark">@log.EndpointType</span></td>
<td class="text-truncate" style="max-width: 300px;">@log.RawPayload</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
@section Scripts {
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.min.js"></script>
<script>
const connection = new signalR.HubConnectionBuilder().withUrl("/syncHub").build();
connection.on("ReceiveLog", function (log) {
const table = document.getElementById("logs-body");
const row = table.insertRow(0);
row.className = "table-success animate__animated animate__fadeIn";
row.innerHTML = `
<td>${new Date(log.receivedAt).toLocaleTimeString()}</td>
<td><span class="badge ${log.endpointType === 'Listen' ? 'bg-info' : 'bg-warning'} text-dark">${log.endpointType}</span></td>
<td class="text-truncate" style="max-width: 300px;">${log.rawPayload}</td>
`;
setTimeout(() => row.classList.remove("table-success"), 2000);
});
connection.on("ReceiveStatus", function (status) {
const statusBadge = document.getElementById("sync-status");
statusBadge.innerText = status;
statusBadge.classList.replace("bg-light", "bg-warning");
setTimeout(() => statusBadge.classList.replace("bg-warning", "bg-light"), 3000);
});
connection.start().catch(err => console.error(err.toString()));
</script>
}