UI timezone

This commit is contained in:
2025-12-08 21:42:10 +01:00
parent 1f95d57717
commit e25cdc4441
9 changed files with 100 additions and 9 deletions

View File

@@ -44,6 +44,7 @@ public static class ServiceCollectionExtensions
services.AddScoped<LayerService>(); services.AddScoped<LayerService>();
services.AddScoped<DataInboxService>(); services.AddScoped<DataInboxService>();
services.AddScoped<JobService>(); services.AddScoped<JobService>();
services.AddScoped<DateTimeHelper>();
// Filter state services (scoped to maintain state during user session) // Filter state services (scoped to maintain state during user session)
services.AddScoped<LayerFilterStateService>(); services.AddScoped<LayerFilterStateService>();

View File

@@ -52,7 +52,7 @@
<RowTemplate Context="row"> <RowTemplate Context="row">
<MudTd DataLabel="Name"><div @oncontextmenu="@(async (e) => await OnRowRightClick(e, row))" @oncontextmenu:preventDefault="true">@row.Name</div></MudTd> <MudTd DataLabel="Name"><div @oncontextmenu="@(async (e) => await OnRowRightClick(e, row))" @oncontextmenu:preventDefault="true">@row.Name</div></MudTd>
<MudTd DataLabel="Source"><div @oncontextmenu="@(async (e) => await OnRowRightClick(e, row))" @oncontextmenu:preventDefault="true">@row.Source</div></MudTd> <MudTd DataLabel="Source"><div @oncontextmenu="@(async (e) => await OnRowRightClick(e, row))" @oncontextmenu:preventDefault="true">@row.Source</div></MudTd>
<MudTd DataLabel="Created At"><div @oncontextmenu="@(async (e) => await OnRowRightClick(e, row))" @oncontextmenu:preventDefault="true">@row.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss")</div></MudTd> <MudTd DataLabel="Created At"><div @oncontextmenu="@(async (e) => await OnRowRightClick(e, row))" @oncontextmenu:preventDefault="true">@DateTimeHelper.FormatDateTime(row.CreatedAt)</div></MudTd>
</RowTemplate> </RowTemplate>
<NoRecordsContent> <NoRecordsContent>
<MudText>No data inbox items to display</MudText> <MudText>No data inbox items to display</MudText>

View File

@@ -15,6 +15,7 @@ public partial class Index : ComponentBase
[Inject] private NavigationManager NavigationManager { get; set; } = default!; [Inject] private NavigationManager NavigationManager { get; set; } = default!;
[Inject] private DataInboxFilterStateService FilterStateService { get; set; } = default!; [Inject] private DataInboxFilterStateService FilterStateService { get; set; } = default!;
[Inject] private IJSRuntime JSRuntime { get; set; } = default!; [Inject] private IJSRuntime JSRuntime { get; set; } = default!;
[Inject] private DateTimeHelper DateTimeHelper { get; set; } = default!;
private PagedResult<DataInboxDto> dataInbox = new(); private PagedResult<DataInboxDto> dataInbox = new();
@@ -23,6 +24,7 @@ public partial class Index : ComponentBase
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
await DateTimeHelper.InitializeAsync();
filterRequest = FilterStateService.FilterRequest; filterRequest = FilterStateService.FilterRequest;
await LoadDataInbox(); await LoadDataInbox();
} }

View File

@@ -6,6 +6,7 @@
@inject EntityChangeHubService HubService @inject EntityChangeHubService HubService
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject ISnackbar Snackbar @inject ISnackbar Snackbar
@inject DateTimeHelper DateTimeHelper
@implements IDisposable @implements IDisposable
<MudCard> <MudCard>
@@ -92,14 +93,14 @@
</MudItem> </MudItem>
<MudItem xs="12" md="6"> <MudItem xs="12" md="6">
<MudTextField Value="@job.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss")" <MudTextField Value="@DateTimeHelper.FormatDateTime(job.CreatedAt)"
Label="Created At" Label="Created At"
Variant="Variant.Outlined" Variant="Variant.Outlined"
ReadOnly="true" ReadOnly="true"
FullWidth="true"/> FullWidth="true"/>
</MudItem> </MudItem>
<MudItem xs="12" md="6"> <MudItem xs="12" md="6">
<MudTextField Value="@(job.LastAttemptAt?.ToString("yyyy-MM-dd HH:mm:ss") ?? "-")" <MudTextField Value="@DateTimeHelper.FormatDateTime(job.LastAttemptAt)"
Label="Last Attempt At" Label="Last Attempt At"
Variant="Variant.Outlined" Variant="Variant.Outlined"
ReadOnly="true" ReadOnly="true"
@@ -107,7 +108,7 @@
</MudItem> </MudItem>
<MudItem xs="12" md="6"> <MudItem xs="12" md="6">
<MudTextField Value="@(job.CompletedAt?.ToString("yyyy-MM-dd HH:mm:ss") ?? "-")" <MudTextField Value="@DateTimeHelper.FormatDateTime(job.CompletedAt)"
Label="Completed At" Label="Completed At"
Variant="Variant.Outlined" Variant="Variant.Outlined"
ReadOnly="true" ReadOnly="true"
@@ -161,6 +162,7 @@
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
await DateTimeHelper.InitializeAsync();
await LoadJob(); await LoadJob();
// Subscribe to SignalR entity changes // Subscribe to SignalR entity changes

View File

@@ -111,12 +111,12 @@
</MudTd> </MudTd>
<MudTd DataLabel="Created"> <MudTd DataLabel="Created">
<div @oncontextmenu="@(async (e) => await OnRowRightClick(e, row))" @oncontextmenu:preventDefault="true"> <div @oncontextmenu="@(async (e) => await OnRowRightClick(e, row))" @oncontextmenu:preventDefault="true">
@row.CreatedAt.ToString("yyyy-MM-dd HH:mm") @DateTimeHelper.FormatDateTime(row.CreatedAt, "yyyy-MM-dd HH:mm")
</div> </div>
</MudTd> </MudTd>
<MudTd DataLabel="Last Attempt"> <MudTd DataLabel="Last Attempt">
<div @oncontextmenu="@(async (e) => await OnRowRightClick(e, row))" @oncontextmenu:preventDefault="true"> <div @oncontextmenu="@(async (e) => await OnRowRightClick(e, row))" @oncontextmenu:preventDefault="true">
@(row.LastAttemptAt?.ToString("yyyy-MM-dd HH:mm") ?? "-") @DateTimeHelper.FormatDateTime(row.LastAttemptAt, "yyyy-MM-dd HH:mm")
</div> </div>
</MudTd> </MudTd>
</RowTemplate> </RowTemplate>

View File

@@ -15,6 +15,7 @@ public partial class Index : ComponentBase, IDisposable
[Inject] private ISnackbar Snackbar { get; set; } = default!; [Inject] private ISnackbar Snackbar { get; set; } = default!;
[Inject] private NavigationManager NavigationManager { get; set; } = default!; [Inject] private NavigationManager NavigationManager { get; set; } = default!;
[Inject] private IJSRuntime JSRuntime { get; set; } = default!; [Inject] private IJSRuntime JSRuntime { get; set; } = default!;
[Inject] private DateTimeHelper DateTimeHelper { get; set; } = default!;
private PagedResult<QueueJob> jobs = new(); private PagedResult<QueueJob> jobs = new();
private bool isLoading = false; private bool isLoading = false;
@@ -25,6 +26,7 @@ public partial class Index : ComponentBase, IDisposable
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
await DateTimeHelper.InitializeAsync();
await LoadJobs(); await LoadJobs();
// Subscribe to SignalR entity changes // Subscribe to SignalR entity changes

View File

@@ -58,7 +58,7 @@
} }
</MudItem> </MudItem>
<MudItem xs="12" md="6"> <MudItem xs="12" md="6">
<MudTextField Value="@layer.CreatedAt.ToString("g")" <MudTextField Value="@DateTimeHelper.FormatDateTime(layer.CreatedAt, "yyyy-MM-dd HH:mm")"
Label="Created" Label="Created"
Variant="Variant.Outlined" Variant="Variant.Outlined"
ReadOnly="true" ReadOnly="true"
@@ -67,7 +67,7 @@
AdornmentText="@(layer.CreatedBy?.Username ?? "")"/> AdornmentText="@(layer.CreatedBy?.Username ?? "")"/>
</MudItem> </MudItem>
<MudItem xs="12" md="6"> <MudItem xs="12" md="6">
<MudTextField Value="@layer.ModifiedAt.ToString("g")" <MudTextField Value="@DateTimeHelper.FormatDateTime(layer.ModifiedAt, "yyyy-MM-dd HH:mm")"
Label="Modified" Label="Modified"
Variant="Variant.Outlined" Variant="Variant.Outlined"
ReadOnly="true" ReadOnly="true"
@@ -316,7 +316,7 @@
<RowTemplate> <RowTemplate>
<MudTd DataLabel="Code">@context.Code</MudTd> <MudTd DataLabel="Code">@context.Code</MudTd>
<MudTd DataLabel="Description">@context.Desc1</MudTd> <MudTd DataLabel="Description">@context.Desc1</MudTd>
<MudTd DataLabel="Modified">@context.ModifiedAt.ToString("g")</MudTd> <MudTd DataLabel="Modified">@DateTimeHelper.FormatDateTime(context.ModifiedAt, "yyyy-MM-dd HH:mm")</MudTd>
<MudTd DataLabel="Modified By">@GetModifiedByUsername(context.ModifiedById)</MudTd> <MudTd DataLabel="Modified By">@GetModifiedByUsername(context.ModifiedById)</MudTd>
</RowTemplate> </RowTemplate>
</MudTable> </MudTable>

View File

@@ -26,6 +26,9 @@ public partial class Details : ComponentBase
[Inject] [Inject]
private ISnackbar Snackbar { get; set; } = null!; private ISnackbar Snackbar { get; set; } = null!;
[Inject]
private DateTimeHelper DateTimeHelper { get; set; } = null!;
private LayerDto? layer; private LayerDto? layer;
private List<RecordDto> records = new(); private List<RecordDto> records = new();
private List<string> displayedColumns = new(); private List<string> displayedColumns = new();
@@ -52,6 +55,7 @@ public partial class Details : ComponentBase
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
await DateTimeHelper.InitializeAsync();
await LoadLayer(); await LoadLayer();
} }

View File

@@ -0,0 +1,80 @@
using Microsoft.JSInterop;
namespace DiunaBI.UI.Shared.Services;
public class DateTimeHelper
{
private readonly IJSRuntime _jsRuntime;
private TimeZoneInfo? _userTimeZone;
private bool _initialized = false;
public DateTimeHelper(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
public async Task InitializeAsync()
{
if (_initialized) return;
try
{
// Get the user's timezone from JavaScript
var timeZoneId = await _jsRuntime.InvokeAsync<string>("eval", "Intl.DateTimeFormat().resolvedOptions().timeZone");
// Try to find the TimeZoneInfo
try
{
_userTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
}
catch
{
// Fallback to local timezone if the IANA timezone ID is not found
_userTimeZone = TimeZoneInfo.Local;
}
}
catch
{
// Fallback to local timezone if JavaScript interop fails
_userTimeZone = TimeZoneInfo.Local;
}
_initialized = true;
}
public string FormatDateTime(DateTime? dateTime, string format = "yyyy-MM-dd HH:mm:ss")
{
if (!dateTime.HasValue)
return "-";
if (!_initialized)
{
// If not initialized yet, just format as-is (will be UTC)
return dateTime.Value.ToString(format);
}
// Convert UTC to user's timezone
var localDateTime = TimeZoneInfo.ConvertTimeFromUtc(dateTime.Value, _userTimeZone ?? TimeZoneInfo.Local);
return localDateTime.ToString(format);
}
public string FormatDate(DateTime? dateTime, string format = "yyyy-MM-dd")
{
return FormatDateTime(dateTime, format);
}
public string FormatTime(DateTime? dateTime, string format = "HH:mm:ss")
{
return FormatDateTime(dateTime, format);
}
public string GetTimeZoneAbbreviation()
{
if (!_initialized || _userTimeZone == null)
return "UTC";
return _userTimeZone.IsDaylightSavingTime(DateTime.Now)
? _userTimeZone.DaylightName
: _userTimeZone.StandardName;
}
}