Add Clock page with live DB time and TimeApiClient service
Introduced a new Clock page that displays and updates the current database server time every second by calling a backend API. Added the TimeApiClient service to handle API requests for the server time. Registered TimeApiClient in Program.cs and updated the navigation menu to include a link to the new Clock page. Includes error handling and custom UI styling for the clock display.
This commit is contained in:
@@ -31,6 +31,12 @@
|
|||||||
<span class="bi bi-table-nav-menu" aria-hidden="true"></span> MassData
|
<span class="bi bi-table-nav-menu" aria-hidden="true"></span> MassData
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="nav-item px-3">
|
||||||
|
<NavLink class="nav-link" href="clock">
|
||||||
|
<span class="bi bi-clock-nav-menu" aria-hidden="true"></span> Clock
|
||||||
|
</NavLink>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
100
DbFirst.BlazorWebApp/Components/Pages/Clock.razor
Normal file
100
DbFirst.BlazorWebApp/Components/Pages/Clock.razor
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
@rendermode InteractiveServer
|
||||||
|
@page "/clock"
|
||||||
|
@inject TimeApiClient TimeApi
|
||||||
|
@implements IAsyncDisposable
|
||||||
|
|
||||||
|
<PageTitle>Clock</PageTitle>
|
||||||
|
|
||||||
|
<h3>DB Server Clock</h3>
|
||||||
|
|
||||||
|
<div class="clock-wrapper">
|
||||||
|
<div class="clock-display @(_error != null ? "clock-error" : "")">
|
||||||
|
@if (_dbTime.HasValue)
|
||||||
|
{
|
||||||
|
<span class="clock-time">@_dbTime.Value.ToString("HH:mm:ss")</span>
|
||||||
|
<span class="clock-date">@_dbTime.Value.ToString("dd.MM.yyyy")</span>
|
||||||
|
}
|
||||||
|
else if (_error != null)
|
||||||
|
{
|
||||||
|
<span class="clock-time">--:--:--</span>
|
||||||
|
<span class="clock-date text-danger">@_error</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="clock-time">...</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.clock-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 40vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clock-display {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--bs-body-bg, #1e1e2e);
|
||||||
|
border: 2px solid var(--bs-border-color, #444);
|
||||||
|
border-radius: 1rem;
|
||||||
|
padding: 2rem 4rem;
|
||||||
|
box-shadow: 0 4px 24px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clock-time {
|
||||||
|
font-size: 5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
color: var(--bs-primary, #0d6efd);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clock-date {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clock-error .clock-time {
|
||||||
|
color: var(--bs-danger, #dc3545);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private DateTime? _dbTime;
|
||||||
|
private string? _error;
|
||||||
|
private Timer? _timer;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await TickAsync();
|
||||||
|
_timer = new Timer(async _ =>
|
||||||
|
{
|
||||||
|
await TickAsync();
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
}, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task TickAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_dbTime = await TimeApi.InsertAndGetLastAsync();
|
||||||
|
_error = null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_error = ex.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
if (_timer != null)
|
||||||
|
await _timer.DisposeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,10 @@ if (!string.IsNullOrWhiteSpace(apiBaseUrl))
|
|||||||
{
|
{
|
||||||
client.BaseAddress = new Uri(apiBaseUrl);
|
client.BaseAddress = new Uri(apiBaseUrl);
|
||||||
});
|
});
|
||||||
|
builder.Services.AddHttpClient<TimeApiClient>(client =>
|
||||||
|
{
|
||||||
|
client.BaseAddress = new Uri(apiBaseUrl);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -38,6 +42,7 @@ else
|
|||||||
builder.Services.AddHttpClient<DashboardApiClient>();
|
builder.Services.AddHttpClient<DashboardApiClient>();
|
||||||
builder.Services.AddHttpClient<MassDataApiClient>();
|
builder.Services.AddHttpClient<MassDataApiClient>();
|
||||||
builder.Services.AddHttpClient<LayoutApiClient>();
|
builder.Services.AddHttpClient<LayoutApiClient>();
|
||||||
|
builder.Services.AddHttpClient<TimeApiClient>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|||||||
27
DbFirst.BlazorWebApp/Services/TimeApiClient.cs
Normal file
27
DbFirst.BlazorWebApp/Services/TimeApiClient.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using System.Net.Http.Json;
|
||||||
|
|
||||||
|
namespace DbFirst.BlazorWebApp.Services;
|
||||||
|
|
||||||
|
public class TimeApiClient
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private const string Endpoint = "api/time";
|
||||||
|
|
||||||
|
public TimeApiClient(HttpClient httpClient)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DateTime?> InsertAndGetLastAsync()
|
||||||
|
{
|
||||||
|
var response = await _httpClient.PostAsync(Endpoint, null);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
var result = await response.Content.ReadFromJsonAsync<TimeResponse>();
|
||||||
|
return result?.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class TimeResponse
|
||||||
|
{
|
||||||
|
public DateTime? Now { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user