refactor(centralui): AuditResultsGrid adopts KeysetPager + AuditFilterBar adopts DateTimeRangeFilter (T35g)

This commit is contained in:
Joseph Doherty
2026-06-18 20:03:40 -04:00
parent a3ac77dd41
commit 1d82e6bc8c
3 changed files with 26 additions and 31 deletions
@@ -91,18 +91,17 @@
<div class="col-auto" data-test="filter-custom-range"> <div class="col-auto" data-test="filter-custom-range">
@if (_model.TimeRange == AuditTimeRangePreset.Custom) @if (_model.TimeRange == AuditTimeRangePreset.Custom)
{ {
<div class="d-flex gap-1 align-items-end"> @* DateTimeRangeFilter is INPUT-ONLY: it surfaces the raw datetime-local
<div> picks (DateTimeKind.Unspecified) straight into the model's range fields.
<label class="form-label small mb-1" for="audit-from">From (UTC)</label> The local→UTC conversion still happens later in Apply()/LocalInputToUtc,
<input id="audit-from" type="datetime-local" class="form-control form-control-sm" exactly as before. IdPrefix="audit" keeps the audit-from/audit-to ids. *@
@bind="_model.CustomFromUtc" /> <DateTimeRangeFilter From="_model.CustomFromUtc"
</div> FromChanged="@(v => _model.CustomFromUtc = v)"
<div> To="_model.CustomToUtc"
<label class="form-label small mb-1" for="audit-to">To (UTC)</label> ToChanged="@(v => _model.CustomToUtc = v)"
<input id="audit-to" type="datetime-local" class="form-control form-control-sm" IdPrefix="audit"
@bind="_model.CustomToUtc" /> FromLabel="From (UTC)"
</div> ToLabel="To (UTC)" />
</div>
} }
else else
{ {
@@ -72,24 +72,20 @@
</table> </table>
</div> </div>
<div class="d-flex justify-content-between align-items-center">
<span class="text-muted small">Page @_pageNumber · @_rows.Count rows</span>
@* CentralUI-032: keyset paging is naturally forward-only, but the @* CentralUI-032: keyset paging is naturally forward-only, but the
in-component _cursorStack lets the user step back through previous in-component _cursorStack lets the user step back through previous
pages by replaying the prior cursor. The Previous button is gated pages by replaying the prior cursor. The Previous button is gated
on the stack having at least one prior cursor — i.e. we are not on on CanGoBack (the stack having at least one prior cursor — i.e. we
the first page. *@ are not on the first page); HasNextPage mirrors the prior "short
<div class="btn-group"> page = end" signal (a full page suggests more rows may follow). All
<button class="btn btn-outline-secondary btn-sm" cursor logic stays page-side; KeysetPager is purely presentational. *@
data-test="grid-prev-page" <KeysetPager PageNumber="_pageNumber"
disabled="@(_loading || !CanGoBack)" RowCount="_rows.Count"
@onclick="PrevPage">Previous page</button> CanGoBack="CanGoBack"
<button class="btn btn-outline-secondary btn-sm" HasNextPage="@(!_loading && _rows.Count >= _pageSize)"
data-test="grid-next-page" Disabled="_loading"
disabled="@(_loading || _rows.Count < _pageSize)" OnPrevious="PrevPage"
@onclick="NextPage">Next page</button> OnNext="NextPage" />
</div>
</div>
</div> </div>
@code { @code {
@@ -95,7 +95,7 @@ public class AuditResultsGridTests : BunitContext
var cut = Render<AuditResultsGrid>(p => p.Add(c => c.Filter, new AuditLogQueryFilter())); var cut = Render<AuditResultsGrid>(p => p.Add(c => c.Filter, new AuditLogQueryFilter()));
cut.Find("[data-test=\"grid-next-page\"]").Click(); cut.Find("[data-test=\"keyset-next\"]").Click();
// Two service calls: initial + next. // Two service calls: initial + next.
Assert.Equal(2, _calls.Count); Assert.Equal(2, _calls.Count);