diff --git a/apps/server/AliasVault.Admin/Main/Pages/Users/Users.razor b/apps/server/AliasVault.Admin/Main/Pages/Users/Users.razor index 30344c25f..8422411db 100644 --- a/apps/server/AliasVault.Admin/Main/Pages/Users/Users.razor +++ b/apps/server/AliasVault.Admin/Main/Pages/Users/Users.razor @@ -1,7 +1,9 @@ @page "/users" @using AliasVault.RazorComponents.Tables +@using AliasVault.RazorComponents.Utilities @using AliasVault.Shared.Server.Services @inject ServerSettingsService SettingsService +@inject NavigationManager NavigationManager @inherits MainBase Users @@ -55,16 +57,11 @@ else @foreach (var user in UserList) { - + @user.CreatedAt.ToString("yyyy-MM-dd HH:mm") - @user.UserName - @user.VaultCount - @user.CredentialCount - @user.EmailClaimCount - @Math.Round((double)user.VaultStorageInKb / 1024, 1) MB - @(user.LastActivityDate?.ToString("yyyy-MM-dd HH:mm") ?? "Never") -
+
+ @user.UserName @if (user.IsInactive) { @@ -75,13 +72,15 @@ else } @if (user.TwoFactorEnabled) { - + }
- - - + @RelativeTimeFormatter.Format(user.LastActivityDate) + @user.CredentialCount + @user.EmailClaimCount + @user.VaultCount + @Math.Round((double)user.VaultStorageInKb / 1024, 1) MB } @@ -92,13 +91,11 @@ else private readonly List _tableColumns = [ new TableColumn { Title = "Registered", PropertyName = "CreatedAt" }, new TableColumn { Title = "Username", PropertyName = "UserName" }, - new TableColumn { Title = "# Vaults", PropertyName = "VaultCount" }, + new TableColumn { Title = "Last Activity", PropertyName = "LastActivityDate" }, new TableColumn { Title = "# Credentials", PropertyName = "CredentialCount" }, new TableColumn { Title = "# Email claims", PropertyName = "EmailClaimCount" }, + new TableColumn { Title = "# Vaults", PropertyName = "VaultCount" }, new TableColumn { Title = "Storage", PropertyName = "VaultStorageInKb" }, - new TableColumn { Title = "Last Activity", PropertyName = "LastActivityDate" }, - new TableColumn { Title = "Status", Sortable = false }, - new TableColumn { Title = "Actions", Sortable = false}, ]; private List UserList { get; set; } = []; @@ -158,6 +155,11 @@ else await RefreshData(CancellationToken.None); } + private void NavigateToUser(string userId) + { + NavigationManager.NavigateTo($"users/{userId}"); + } + /// protected override async Task OnInitializedAsync() { diff --git a/apps/server/Shared/AliasVault.RazorComponents/Tables/SortableTable.razor b/apps/server/Shared/AliasVault.RazorComponents/Tables/SortableTable.razor index e9da25b53..3e6c81519 100644 --- a/apps/server/Shared/AliasVault.RazorComponents/Tables/SortableTable.razor +++ b/apps/server/Shared/AliasVault.RazorComponents/Tables/SortableTable.razor @@ -1,5 +1,5 @@ -
- +
+
@foreach (var column in Columns) diff --git a/apps/server/Shared/AliasVault.RazorComponents/Tables/SortableTableRow.razor b/apps/server/Shared/AliasVault.RazorComponents/Tables/SortableTableRow.razor index 1bbdc2a02..d3b67dd28 100644 --- a/apps/server/Shared/AliasVault.RazorComponents/Tables/SortableTableRow.razor +++ b/apps/server/Shared/AliasVault.RazorComponents/Tables/SortableTableRow.razor @@ -1,4 +1,4 @@ - + @ChildContent diff --git a/apps/server/Shared/AliasVault.RazorComponents/Utilities/RelativeTimeFormatter.cs b/apps/server/Shared/AliasVault.RazorComponents/Utilities/RelativeTimeFormatter.cs new file mode 100644 index 000000000..3a785b9cc --- /dev/null +++ b/apps/server/Shared/AliasVault.RazorComponents/Utilities/RelativeTimeFormatter.cs @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) aliasvault. All rights reserved. +// Licensed under the AGPLv3 license. See LICENSE.md file in the project root for full license information. +// +//----------------------------------------------------------------------- + +namespace AliasVault.RazorComponents.Utilities; + +/// +/// Utility for formatting DateTime values as relative time strings (e.g., "5 minutes ago", "2 days ago"). +/// +public static class RelativeTimeFormatter +{ + /// + /// Formats a DateTime as a relative time string. + /// + /// The DateTime to format (assumed to be UTC). + /// A human-readable relative time string. + public static string Format(DateTime dateTime) + { + var now = DateTime.UtcNow; + var timeSpan = now - dateTime; + + if (timeSpan.TotalSeconds < 0) + { + return "just now"; + } + + if (timeSpan.TotalSeconds < 60) + { + var seconds = (int)timeSpan.TotalSeconds; + return seconds <= 5 ? "just now" : $"{seconds} sec ago"; + } + + if (timeSpan.TotalMinutes < 60) + { + var minutes = (int)timeSpan.TotalMinutes; + return minutes == 1 ? "1 min ago" : $"{minutes} min ago"; + } + + if (timeSpan.TotalHours < 24) + { + var hours = (int)timeSpan.TotalHours; + return hours == 1 ? "1 hour ago" : $"{hours} hours ago"; + } + + if (timeSpan.TotalDays < 30) + { + var days = (int)timeSpan.TotalDays; + return days == 1 ? "1 day ago" : $"{days} days ago"; + } + + if (timeSpan.TotalDays < 365) + { + var months = (int)(timeSpan.TotalDays / 30); + return months == 1 ? "1 month ago" : $"{months} months ago"; + } + + var years = (int)(timeSpan.TotalDays / 365); + return years == 1 ? "1 year ago" : $"{years} years ago"; + } + + /// + /// Formats a nullable DateTime as a relative time string. + /// + /// The nullable DateTime to format. + /// The fallback string to use if dateTime is null. + /// A human-readable relative time string or the fallback value. + public static string Format(DateTime? dateTime, string fallback = "Never") + { + return dateTime.HasValue ? Format(dateTime.Value) : fallback; + } +}