mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-13 09:55:33 -04:00
Allow mobile to scroll in admin tables, update users table structure (#1705)
This commit is contained in:
committed by
Leendert de Borst
parent
d2e0b20437
commit
9e459fc664
@@ -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
|
||||
|
||||
<LayoutPageTitle>Users</LayoutPageTitle>
|
||||
@@ -55,16 +57,11 @@ else
|
||||
<SortableTable Columns="@_tableColumns" SortColumn="@SortColumn" SortDirection="@SortDirection" OnSortChanged="HandleSortChanged">
|
||||
@foreach (var user in UserList)
|
||||
{
|
||||
<SortableTableRow>
|
||||
<SortableTableRow OnClick="@(() => NavigateToUser(user.Id))">
|
||||
<SortableTableColumn IsPrimary="true">@user.CreatedAt.ToString("yyyy-MM-dd HH:mm")</SortableTableColumn>
|
||||
<SortableTableColumn>@user.UserName</SortableTableColumn>
|
||||
<SortableTableColumn>@user.VaultCount</SortableTableColumn>
|
||||
<SortableTableColumn>@user.CredentialCount</SortableTableColumn>
|
||||
<SortableTableColumn>@user.EmailClaimCount</SortableTableColumn>
|
||||
<SortableTableColumn>@Math.Round((double)user.VaultStorageInKb / 1024, 1) MB</SortableTableColumn>
|
||||
<SortableTableColumn>@(user.LastActivityDate?.ToString("yyyy-MM-dd HH:mm") ?? "Never")</SortableTableColumn>
|
||||
<SortableTableColumn>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<span>@user.UserName</span>
|
||||
@if (user.IsInactive)
|
||||
{
|
||||
<StatusPill Enabled="false" TextFalse="Inactive" />
|
||||
@@ -75,13 +72,15 @@ else
|
||||
}
|
||||
@if (user.TwoFactorEnabled)
|
||||
{
|
||||
<StatusPill Enabled="true" TextTrue="2FA enabled" />
|
||||
<StatusPill Enabled="true" TextTrue="2FA" />
|
||||
}
|
||||
</div>
|
||||
</SortableTableColumn>
|
||||
<SortableTableColumn>
|
||||
<LinkButton Color="primary" Href="@($"users/{user.Id}")" Text="View" />
|
||||
</SortableTableColumn>
|
||||
<SortableTableColumn>@RelativeTimeFormatter.Format(user.LastActivityDate)</SortableTableColumn>
|
||||
<SortableTableColumn>@user.CredentialCount</SortableTableColumn>
|
||||
<SortableTableColumn>@user.EmailClaimCount</SortableTableColumn>
|
||||
<SortableTableColumn>@user.VaultCount</SortableTableColumn>
|
||||
<SortableTableColumn>@Math.Round((double)user.VaultStorageInKb / 1024, 1) MB</SortableTableColumn>
|
||||
</SortableTableRow>
|
||||
}
|
||||
</SortableTable>
|
||||
@@ -92,13 +91,11 @@ else
|
||||
private readonly List<TableColumn> _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<UserViewModel> UserList { get; set; } = [];
|
||||
@@ -158,6 +155,11 @@ else
|
||||
await RefreshData(CancellationToken.None);
|
||||
}
|
||||
|
||||
private void NavigateToUser(string userId)
|
||||
{
|
||||
NavigationManager.NavigateTo($"users/{userId}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="rounded-lg border border-gray-200 dark:border-gray-700 shadow-sm overflow-hidden">
|
||||
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
|
||||
<div class="rounded-lg border border-gray-200 dark:border-gray-700 shadow-sm overflow-x-auto">
|
||||
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400 min-w-max">
|
||||
<thead class="text-xs text-gray-700 bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
||||
<tr>
|
||||
@foreach (var column in Columns)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<tr @onclick="HandleClick" class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 @(Class)">
|
||||
<tr @onclick="HandleClick" class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 @(OnClick.HasDelegate ? "cursor-pointer" : "") @(Class)">
|
||||
@ChildContent
|
||||
</tr>
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="RelativeTimeFormatter.cs" company="aliasvault">
|
||||
// Copyright (c) aliasvault. All rights reserved.
|
||||
// Licensed under the AGPLv3 license. See LICENSE.md file in the project root for full license information.
|
||||
// </copyright>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace AliasVault.RazorComponents.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// Utility for formatting DateTime values as relative time strings (e.g., "5 minutes ago", "2 days ago").
|
||||
/// </summary>
|
||||
public static class RelativeTimeFormatter
|
||||
{
|
||||
/// <summary>
|
||||
/// Formats a DateTime as a relative time string.
|
||||
/// </summary>
|
||||
/// <param name="dateTime">The DateTime to format (assumed to be UTC).</param>
|
||||
/// <returns>A human-readable relative time string.</returns>
|
||||
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";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a nullable DateTime as a relative time string.
|
||||
/// </summary>
|
||||
/// <param name="dateTime">The nullable DateTime to format.</param>
|
||||
/// <param name="fallback">The fallback string to use if dateTime is null.</param>
|
||||
/// <returns>A human-readable relative time string or the fallback value.</returns>
|
||||
public static string Format(DateTime? dateTime, string fallback = "Never")
|
||||
{
|
||||
return dateTime.HasValue ? Format(dateTime.Value) : fallback;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user