diff --git a/ZeroSpace/ZeroSpace.Web/Pages/Units.razor b/ZeroSpace/ZeroSpace.Web/Pages/Units.razor
index eeba544..cf9e935 100644
--- a/ZeroSpace/ZeroSpace.Web/Pages/Units.razor
+++ b/ZeroSpace/ZeroSpace.Web/Pages/Units.razor
@@ -1,8 +1,6 @@
-@page "/units"
+@page "/units"
@using ZeroSpace.Model
@using ZeroSpace.Web.Services
-@using Telerik.Blazor
-@using Telerik.Blazor.Components
@inject IUnitsApiClient UnitsApiClient
Units
@@ -29,7 +27,7 @@ else if (errorMessage != null)
}
-else if (gearNotes == null || gearNotes.Count == 0)
+else if (units == null || units.Count == 0)
{
No units found.
@@ -37,42 +35,159 @@ else if (gearNotes == null || gearNotes.Count == 0)
}
else
{
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ Page @(page) of @TotalPages (@SortedUnits.Count units)
+
}
@code {
- private List
? gearNotes;
+ private List? units;
private bool isLoading = true;
private string? errorMessage;
+ private string? sortKey;
+ private bool sortAscending = true;
+ private int page = 1;
+ private const int PageSize = 50;
+ private readonly Dictionary filters = new();
+
+ private sealed record ColumnDef(
+ string Key,
+ string Title,
+ string Width,
+ Func SortValue,
+ Func Display);
+
+ private static readonly IReadOnlyList Columns = new List
+ {
+ new("Name", "Name", "200px", u => u.Name, u => u.Name),
+ new("Hexite", "Hexite", "90px", u => u.Hexite, u => u.Hexite.ToString()),
+ new("Flux", "Flux", "90px", u => u.Flux, u => u.Flux.ToString()),
+ new("DpsPerTotalCost", "DpsPerTotalCost", "90px", u => u.DpsPerTotalCost, u => u.DpsPerTotalCost.ToString()),
+ new("HealthPerTotalCost", "HealthPerTotalCost", "90px", u => u.HealthPerTotalCost, u => u.HealthPerTotalCost.ToString()),
+ new("Supply", "Supply", "90px", u => u.Supply, u => u.Supply.ToString()),
+ new("ProductionTime", "Production Time", "90px", u => u.ProductionTime, u => u.ProductionTime.ToString()),
+ new("Health", "Health", "90px", u => u.Health, u => u.Health.ToString()),
+ new("Shields", "Shields", "90px", u => u.Shields, u => u.Shields.ToString()),
+ new("ArmorRating", "Armor Rating", "90px", u => u.ArmorRating, u => u.ArmorRating.ToString()),
+ new("MovementSpeed", "Movement Speed", "90px", u => u.MovementSpeed, u => u.MovementSpeed.ToString()),
+ new("DamagePerSecond", "Damage Per Second", "90px", u => u.DamagePerSecond, u => u.DamagePerSecond.ToString()),
+ new("AttackRange", "Attack Range", "90px", u => u.AttackRange, u => u.AttackRange.ToString()),
+ new("Attributes", "Attributes", "90px", u => string.Join(", ", u.Attributes), u => string.Join(", ", u.Attributes)),
+ new("Tier", "Tier", "90px", u => u.Tier, u => u.Tier.ToString()),
+ new("Faction", "Faction", "90px", u => u.Faction, u => u.Faction),
+ new("Hotkey", "Hotkey", "90px", u => u.Hotkey, u => u.Hotkey ?? string.Empty),
+ new("BuildAtSameTime", "Build At Same Time", "90px", u => u.BuildAtSameTime, u => u.BuildAtSameTime.ToString()),
+ new("Limit", "Limit", "90px", u => u.Limit, u => u.Limit?.ToString() ?? string.Empty),
+ };
+
+ private List SortedUnits
+ {
+ get
+ {
+ var query = (units ?? new List()).AsEnumerable();
+
+ foreach (var col in Columns)
+ {
+ if (filters.TryGetValue(col.Key, out var text) && !string.IsNullOrWhiteSpace(text))
+ {
+ query = query.Where(u => col.Display(u).Contains(text, StringComparison.OrdinalIgnoreCase));
+ }
+ }
+
+ var sortCol = Columns.FirstOrDefault(c => c.Key == sortKey);
+ if (sortCol != null)
+ {
+ query = sortAscending
+ ? query.OrderBy(u => sortCol.SortValue(u))
+ : query.OrderByDescending(u => sortCol.SortValue(u));
+ }
+
+ return query.ToList();
+ }
+ }
+
+ private int TotalPages => Math.Max(1, (int)Math.Ceiling(SortedUnits.Count / (double)PageSize));
+
+ private List PagedUnits => SortedUnits.Skip((page - 1) * PageSize).Take(PageSize).ToList();
+
+ private void ToggleSort(string key)
+ {
+ if (sortKey == key)
+ {
+ sortAscending = !sortAscending;
+ }
+ else
+ {
+ sortKey = key;
+ sortAscending = true;
+ }
+ page = 1;
+ }
+
+ private string SortGlyph(string key) => sortKey == key ? (sortAscending ? "▲" : "▼") : string.Empty;
+
+ private string GetFilter(string key) => filters.TryGetValue(key, out var value) ? value : string.Empty;
+
+ private void SetFilter(string key, string? value)
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ filters.Remove(key);
+ }
+ else
+ {
+ filters[key] = value;
+ }
+ page = 1;
+ }
+
+ private void ChangePage(int newPage)
+ {
+ page = Math.Clamp(newPage, 1, TotalPages);
+ }
+
protected override async Task OnInitializedAsync()
{
await LoadUnitsAsync();
@@ -85,8 +200,8 @@ else
try
{
- var units = await UnitsApiClient.GetUnitsAsync();
- gearNotes = units.ToList();
+ var result = await UnitsApiClient.GetUnitsAsync();
+ units = result.ToList();
}
catch (Exception ex)
{