Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .claude/features-done/blazor-ui-overhaul.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Feature: blazor-ui-overhaul

## Goal
Overhaul the Blazor monitoring UI: slim ListView columns, move details to a dialog showing all metadata + JSON content, show persist type per cache type, and make Clear Cache refresh the ListView.

## Scope
- `CacheTypeInfo` — add `PersistType` field
- `CacheMonitor.Set/Track` — populate `PersistType`
- `ListView.razor` — slim columns to Key, Expires, Size, Load Time, Stale; add persist type to the type-level grid; add chevron button per row; subscribe to monitor events for live refresh
- `SummaryView.razor` — subscribe to monitor events for live refresh
- New `DetailView.razor` — dialog component with all metadata + JSON content via `PeekAsync` + reflection + `CopyButton`

## Acceptance Criteria
- ListView shows slim columns with chevron to open detail dialog
- Detail dialog shows all metadata (Created, Updated, Fresh Span, Last Accessed, Access Count) plus cached data as JSON
- Copy JSON button works
- Persist type column appears on the type-level grid
- Clear Cache button refreshes the ListView without reload
- All tests still pass

## Done Condition
User confirms the UI works as expected.

## Originating Branch
develop
9 changes: 5 additions & 4 deletions Sample/Tharga.Cache.BlazorServer/Components/App.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="en">

<head>
Expand All @@ -11,14 +11,15 @@
<link rel="stylesheet" href="@Assets["Tharga.Cache.BlazorServer.styles.css"]" />
<ImportMap />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet />
<RadzenTheme Theme="material" />
<HeadOutlet @rendermode="RenderMode.InteractiveServer" />
<RadzenTheme Theme="material" @rendermode="new InteractiveServerRenderMode(prerender: false)" />
</head>

<body>
<Routes />
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />
<ReconnectModal />
<script src="@Assets["_framework/blazor.web.js"]"></script>
<script src="@Assets["_content/Tharga.Blazor/tharga.blazor.js"]"></script>
<script src="_content/Radzen.Blazor/Radzen.Blazor.js?v=@(typeof(Radzen.Colors).Assembly.GetName().Version)"></script>
</body>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@
<a href="." class="reload">Reload</a>
<span class="dismiss">🗙</span>
</div>

<RadzenComponents />
2 changes: 2 additions & 0 deletions Sample/Tharga.Cache.BlazorServer/Program.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Blazored.LocalStorage;
using Radzen;
using Tharga.Cache;
using Tharga.Cache.BlazorServer.Components;
Expand All @@ -13,6 +14,7 @@
.AddInteractiveServerComponents();

builder.Services.AddRadzenComponents();
builder.Services.AddBlazoredLocalStorage();
builder.AddMongoDB();
builder.Services.AddCache(o =>
{
Expand Down
84 changes: 84 additions & 0 deletions Tharga.Cache.Blazor/CacheContent.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
@using System.Reflection
@using System.Text.Json
@using Microsoft.JSInterop
@inject IEternalCache Cache
@inject IJSRuntime Js
@inject NotificationService NotificationService

<div style="position:relative;">
<div style="position:absolute; top:0; right:0;">
<RadzenButton Icon="content_copy" Size="ButtonSize.Small" ButtonStyle="ButtonStyle.Light"
Click="CopyAction" Disabled="@_loading" title="Copy JSON" />
</div>
@if (_loading)
{
<Loading />
}
else
{
<pre style="background:#f5f5f5; padding:0.75rem; padding-right:3rem; border-radius:4px; max-height:400px; overflow:auto; margin:0;">@_json</pre>
}
</div>

@code {
[Parameter] public Type CacheType { get; set; }
[Parameter] public string CacheKey { get; set; }

private string _json;
private bool _loading = true;

protected override async Task OnInitializedAsync()
{
await LoadContentAsync();
}

private async Task LoadContentAsync()
{
_loading = true;
try
{
if (CacheType == null || string.IsNullOrEmpty(CacheKey))
{
_json = "(no key/type)";
return;
}

var method = typeof(ICache).GetMethod(nameof(ICache.PeekAsync), BindingFlags.Public | BindingFlags.Instance);
if (method == null)
{
_json = "(PeekAsync method not found)";
return;
}

var generic = method.MakeGenericMethod(CacheType);
Key key = CacheKey;
var task = (Task)generic.Invoke(Cache, [key]);
await task.ConfigureAwait(false);
var resultProperty = task.GetType().GetProperty("Result");
var data = resultProperty?.GetValue(task);

_json = data == null
? "(no data)"
: JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true });
}
catch (Exception ex)
{
_json = $"(error: {ex.Message})";
}
finally
{
_loading = false;
}
}

private async Task CopyAction()
{
if (string.IsNullOrWhiteSpace(_json)) return;
await Js.InvokeVoidAsync("clipboard.copyText", _json);
NotificationService.Notify(new NotificationMessage
{
Summary = "Content copied.",
Severity = NotificationSeverity.Info
});
}
}
66 changes: 66 additions & 0 deletions Tharga.Cache.Blazor/DetailView.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@

<RadzenStack Orientation="Orientation.Vertical" Gap="1rem">
<RadzenRow>
<RadzenColumn Size="6">
<RadzenText TextStyle="TextStyle.Caption">Key</RadzenText>
<RadzenText TextStyle="TextStyle.Body1">@CacheKey</RadzenText>
</RadzenColumn>
<RadzenColumn Size="6">
<RadzenText TextStyle="TextStyle.Caption">Type</RadzenText>
<RadzenText TextStyle="TextStyle.Body1">@CacheType?.Name</RadzenText>
</RadzenColumn>
</RadzenRow>
<RadzenRow>
<RadzenColumn Size="4">
<RadzenText TextStyle="TextStyle.Caption">Created</RadzenText>
<div><DateTimeView Date="@Info?.CreateTime" /></div>
</RadzenColumn>
<RadzenColumn Size="4">
<RadzenText TextStyle="TextStyle.Caption">Updated</RadzenText>
<div><DateTimeView Date="@Info?.UpdateTime" /></div>
</RadzenColumn>
<RadzenColumn Size="4">
<RadzenText TextStyle="TextStyle.Caption">Last Accessed</RadzenText>
<div><DateTimeView Date="@Info?.LastAccessTime" /></div>
</RadzenColumn>
</RadzenRow>
<RadzenRow>
<RadzenColumn Size="4">
<RadzenText TextStyle="TextStyle.Caption">Fresh Span</RadzenText>
<div><TimeSpanView TimeSpan="@Info?.FreshSpan" /></div>
</RadzenColumn>
<RadzenColumn Size="4">
<RadzenText TextStyle="TextStyle.Caption">Expires</RadzenText>
<div><DateTimeView Date="@Info?.ExpireTime" /></div>
</RadzenColumn>
<RadzenColumn Size="4">
<RadzenText TextStyle="TextStyle.Caption">Load Time</RadzenText>
<div><TimeSpanView TimeSpan="@Info?.LoadDuration" /></div>
</RadzenColumn>
</RadzenRow>
<RadzenRow>
<RadzenColumn Size="4">
<RadzenText TextStyle="TextStyle.Caption">Size</RadzenText>
<RadzenText TextStyle="TextStyle.Body1">@(Info?.Size.ToReadableByteSize() ?? "-")</RadzenText>
</RadzenColumn>
<RadzenColumn Size="4">
<RadzenText TextStyle="TextStyle.Caption">Access Count</RadzenText>
<RadzenText TextStyle="TextStyle.Body1">@(Info?.AccessCount.ToString() ?? "-")</RadzenText>
</RadzenColumn>
<RadzenColumn Size="4">
<RadzenText TextStyle="TextStyle.Caption">Stale</RadzenText>
<RadzenText TextStyle="TextStyle.Body1">@(Info?.IsStale.ToString() ?? "-")</RadzenText>
</RadzenColumn>
</RadzenRow>
<ExpandableCard Text="Content" Icon="data_object" AllowSaveState="false">
<ChildContent>
<CacheContent CacheType="@CacheType" CacheKey="@CacheKey" />
</ChildContent>
</ExpandableCard>
</RadzenStack>

@code {
[Parameter] public Type CacheType { get; set; }
[Parameter] public string CacheKey { get; set; }
[Parameter] public CacheItemInfo Info { get; set; }
}
76 changes: 50 additions & 26 deletions Tharga.Cache.Blazor/ListView.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
@inject ICacheMonitor CacheMonitor
@implements IDisposable
@inject ICacheMonitor CacheMonitor
@inject DialogService DialogService

@if (_model == null)
{
Expand All @@ -23,31 +25,11 @@ else
<span title="@item.Key">@item.Key</span>
</Template>
</RadzenDataGridColumn>
<RadzenDataGridColumn Title="Created" SortProperty="Value.CreateTime">
<Template>
<DateTimeView Date="@context.Value.CreateTime"/>
</Template>
</RadzenDataGridColumn>
<RadzenDataGridColumn Title="Updated" SortProperty="Value.UpdateTime">
<Template>
<DateTimeView Date="@context.Value.UpdateTime"/>
</Template>
</RadzenDataGridColumn>
<RadzenDataGridColumn Title="Fresh Span" SortProperty="Value.FreshSpan">
<Template>
<TimeSpanView TimeSpan="@context.Value.FreshSpan"/>
</Template>
</RadzenDataGridColumn>
<RadzenDataGridColumn Title="Expires" SortProperty="Value.ExpireTime">
<Template>
<DateTimeView Date="@context.Value.ExpireTime"/>
</Template>
</RadzenDataGridColumn>
<RadzenDataGridColumn Title="Last Accessed" SortProperty="Value.LastAccessTime">
<Template>
<DateTimeView Date="@context.Value.LastAccessTime"/>
</Template>
</RadzenDataGridColumn>
<RadzenDataGridColumn Title="Load Time" SortProperty="Value.LoadDuration">
<Template>
<TimeSpanView TimeSpan="@context.Value.LoadDuration"/>
Expand All @@ -58,12 +40,13 @@ else
@item.Value.Size.ToReadableByteSize()
</Template>
</RadzenDataGridColumn>
<RadzenDataGridColumn Title="Access Count" SortProperty="Value.AccessCount">
<Template>
@context.Value.AccessCount
<RadzenDataGridColumn Title="Stale" Property="Value.IsStale" Sortable="false"/>
<RadzenDataGridColumn Title="" Sortable="false" Filterable="false" Width="48px" TextAlign="TextAlign.Right">
<Template Context="item">
<RadzenButton Icon="article" ButtonStyle="ButtonStyle.Light" Size="ButtonSize.Small"
Click="@(() => OpenDetailAsync(cacheType.Type, item.Key, item.Value))" />
</Template>
</RadzenDataGridColumn>
<RadzenDataGridColumn Title="Stale" Property="Value.IsStale" Sortable="false"/>
</Columns>
</RadzenDataGrid>
</Template>
Expand All @@ -73,6 +56,11 @@ else
@context.Type.Name
</Template>
</RadzenDataGridColumn>
<RadzenDataGridColumn Title="Persist">
<Template>
@FormatPersistType(context.PersistType)
</Template>
</RadzenDataGridColumn>
<RadzenDataGridColumn Title="Count">
<Template>
@context.Items.Count
Expand All @@ -87,10 +75,46 @@ else
private CacheTypeInfo[] _model;

protected override Task OnInitializedAsync()
{
Refresh();
CacheMonitor.DataSetEvent += OnCacheChanged;
CacheMonitor.DataDropEvent += OnCacheChanged;
return base.OnInitializedAsync();
}

public void Dispose()
{
CacheMonitor.DataSetEvent -= OnCacheChanged;
CacheMonitor.DataDropEvent -= OnCacheChanged;
}

private void OnCacheChanged(object sender, EventArgs e)
{
Refresh();
InvokeAsync(StateHasChanged);
}

private void Refresh()
{
_model = CacheMonitor.GetInfos().ToArray();
}

return base.OnInitializedAsync();
private static string FormatPersistType(Type persistType)
{
if (persistType == null) return "-";
var name = persistType.Name;
return name.StartsWith("I") && name.Length > 1 && char.IsUpper(name[1]) ? name.Substring(1) : name;
}

private async Task OpenDetailAsync(Type type, string key, CacheItemInfo info)
{
await DialogService.OpenAsync<DetailView>($"Cache: {key}",
new Dictionary<string, object>
{
["CacheType"] = type,
["CacheKey"] = key,
["Info"] = info
},
new DialogOptions { Width = "800px", Resizable = true, Draggable = true });
}
}
18 changes: 16 additions & 2 deletions Tharga.Cache.Blazor/SummaryView.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@inject ICacheMonitor CacheMonitor
@implements IDisposable
@inject ICacheMonitor CacheMonitor

<RadzenRow>
<RadzenColumn>
Expand Down Expand Up @@ -28,10 +29,23 @@
protected override Task OnInitializedAsync()
{
LoadCacheInfo();

CacheMonitor.DataSetEvent += OnCacheChanged;
CacheMonitor.DataDropEvent += OnCacheChanged;
return base.OnInitializedAsync();
}

public void Dispose()
{
CacheMonitor.DataSetEvent -= OnCacheChanged;
CacheMonitor.DataDropEvent -= OnCacheChanged;
}

private void OnCacheChanged(object sender, EventArgs e)
{
LoadCacheInfo();
InvokeAsync(StateHasChanged);
}

private void LoadCacheInfo()
{
_cacheCount = CacheMonitor.GetInfos()?.SelectMany(x => x.Items).Count() ?? 0;
Expand Down
4 changes: 2 additions & 2 deletions Tharga.Cache.File.Tests/Tharga.Cache.File.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.msbuild" Version="8.0.1">
<PackageReference Include="coverlet.msbuild" Version="10.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="8.0.1">
<PackageReference Include="coverlet.collector" Version="10.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
4 changes: 2 additions & 2 deletions Tharga.Cache.MongoDB.Tests/Tharga.Cache.MongoDB.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.msbuild" Version="8.0.1">
<PackageReference Include="coverlet.msbuild" Version="10.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="8.0.1">
<PackageReference Include="coverlet.collector" Version="10.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Loading
Loading