mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-30 08:36:55 +09:00
fix(views): the view bar counted rows the table was not showing
Every page built its own "N of M" from its OWN array, which is only search-filtered -- the column rules are applied inside DataTable. So the bar read "4 of 4" while the table showed 2, on every page, the moment any rule was active. Moving filtering into the columns is what introduced it. useTableChrome now exposes `shownRows`/`shownCount`, computed with the same engine the table uses, so the two cannot disagree. Fifteen call sites moved onto it, plus the DataTableFooter counts that told the same lie. ResourceListPage had a worse version: it sliced `items` for the page size and DataTable filtered afterwards, so a page size of 25 took 25 UNFILTERED rows and then filtered them -- "show 25" could render three. It pages `chrome.shownRows` now, so the slice and the render agree. The Security tabs already computed this correctly by calling applyFilters themselves; they are left alone rather than churned. Guarded going forward: the preset prober now reads `.sk-viewbar__count` after selecting each view and fails on !BARCOUNT if the number disagrees with the rendered rows. 0 mismatches across 11 targets. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
086073d7e5
commit
002fb1bccb
16 files changed
+41
-25
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import useGridViews from './useGridViews';
|
||||
import { OPS, emptyValueFor, isFilterable, ruleId, withInferredTypes } from './fields';
|
||||
import { OPS, applyFilters, emptyValueFor, isFilterable, ruleId, withInferredTypes } from './fields';
|
||||
|
||||
const NO_FILTERS = { match: 'all', rules: [] };
|
||||
|
||||
@@ -149,12 +149,24 @@ export function useTableChrome({
|
||||
resetToView: api.resetToView,
|
||||
});
|
||||
|
||||
// What the table will ACTUALLY render. `rows` is the page's own already
|
||||
// searched/filtered array; the column rules are applied by <DataTable>
|
||||
// itself, so a page counting `rows.length` for its view bar reported "4 of
|
||||
// 4" while the table showed 2. Computed here, once, from the same engine
|
||||
// the table uses, so the two can never disagree.
|
||||
const shownRows = useMemo(
|
||||
() => applyFilters(rows, filters, orderedColumns),
|
||||
[rows, filters, orderedColumns],
|
||||
);
|
||||
|
||||
return {
|
||||
cfg,
|
||||
api,
|
||||
views,
|
||||
columns: orderedColumns,
|
||||
noun,
|
||||
shownRows,
|
||||
shownCount: shownRows.length,
|
||||
filterCount: filters.rules.length,
|
||||
drawerOpen,
|
||||
setDrawerOpen,
|
||||
|
||||
@@ -173,11 +173,6 @@ export default function ResourceListPage({
|
||||
}
|
||||
};
|
||||
|
||||
const pagedItems = useMemo(
|
||||
() => (pageSize === 'all' ? items : items.slice(0, pageSize)),
|
||||
[items, pageSize],
|
||||
);
|
||||
|
||||
// The chrome — view picker, chip bar, filter drawer, tools menu, shareable
|
||||
// links — comes from the shared hook rather than being adapted again here.
|
||||
// This wrapper used to carry its own copy of that adapter, which is how it
|
||||
@@ -218,6 +213,15 @@ export default function ResourceListPage({
|
||||
const orderedColumns = chrome.columns;
|
||||
const tableViews = chrome.views;
|
||||
|
||||
// Paged AFTER the column rules, not before. Slicing `items` meant a page
|
||||
// size of 25 took 25 unfiltered rows and THEN filtered them, so "show 25"
|
||||
// could render three. `chrome.shownRows` is the same set the table renders,
|
||||
// so the two agree; DataTable re-applying the rules to it is a no-op.
|
||||
const pagedItems = useMemo(
|
||||
() => (pageSize === 'all' ? chrome.shownRows : chrome.shownRows.slice(0, pageSize)),
|
||||
[chrome.shownRows, pageSize],
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
// Same wrapper as the loaded state below. A skeleton that renders
|
||||
// outside the page's padded container occupies a different box than the
|
||||
@@ -260,7 +264,7 @@ export default function ResourceListPage({
|
||||
<GridViewPicker
|
||||
views={tableViews}
|
||||
label={noun}
|
||||
total={`${items.length} of ${resolvedTotal} ${noun}`}
|
||||
total={`${chrome.shownCount} of ${resolvedTotal} ${noun}`}
|
||||
onCreate={chrome.createView}
|
||||
/>
|
||||
)}
|
||||
@@ -365,7 +369,7 @@ export default function ResourceListPage({
|
||||
footer={(
|
||||
<DataTableFooter
|
||||
shown={pagedItems.length}
|
||||
total={items.length}
|
||||
total={chrome.shownCount}
|
||||
noun={noun.replace(/s$/, '')}
|
||||
pageSize={pageSize}
|
||||
onPageSizeChange={changePageSize}
|
||||
|
||||
@@ -293,7 +293,7 @@ const ApiKeysSection = () => {
|
||||
<GridViewPicker
|
||||
views={chrome.views}
|
||||
label="keys"
|
||||
total={loading ? null : `${keys.length} key${keys.length === 1 ? '' : 's'}`}
|
||||
total={loading ? null : `${chrome.shownCount} of ${keys.length} key${keys.length === 1 ? '' : 's'}`}
|
||||
onCreate={chrome.createView}
|
||||
/>
|
||||
<ListToolbar
|
||||
|
||||
@@ -277,7 +277,7 @@ const InvitationsTab = () => {
|
||||
<GridViewPicker
|
||||
views={chrome.views}
|
||||
label="invitations"
|
||||
total={loading ? null : `${invitations.length} invitation${invitations.length === 1 ? '' : 's'}`}
|
||||
total={loading ? null : `${chrome.shownCount} of ${invitations.length} invitation${invitations.length === 1 ? '' : 's'}`}
|
||||
onCreate={chrome.createView}
|
||||
/>
|
||||
<ListToolbar
|
||||
|
||||
@@ -309,7 +309,7 @@ const MigrationHistoryTab = () => {
|
||||
<GridViewPicker
|
||||
views={chrome.views}
|
||||
label="migrations"
|
||||
total={`${revisions.length} revision${revisions.length === 1 ? '' : 's'}`}
|
||||
total={`${chrome.shownCount} of ${revisions.length} revision${revisions.length === 1 ? '' : 's'}`}
|
||||
onCreate={chrome.createView}
|
||||
/>
|
||||
<ListToolbar
|
||||
|
||||
@@ -360,7 +360,7 @@ const UsersTab = () => {
|
||||
<GridViewPicker
|
||||
views={chrome.views}
|
||||
label="users"
|
||||
total={`${users.length} user${users.length === 1 ? '' : 's'}`}
|
||||
total={`${chrome.shownCount} of ${users.length} user${users.length === 1 ? '' : 's'}`}
|
||||
onCreate={chrome.createView}
|
||||
/>
|
||||
<ListToolbar
|
||||
|
||||
@@ -467,7 +467,7 @@ const CronJobs = () => {
|
||||
<GridViewPicker
|
||||
views={chrome.views}
|
||||
label="jobs"
|
||||
total={`${shown.length} of ${jobs.length} jobs`}
|
||||
total={`${chrome.shownCount} of ${jobs.length} jobs`}
|
||||
onCreate={chrome.createView}
|
||||
/>
|
||||
<ListToolbar
|
||||
@@ -503,7 +503,7 @@ const CronJobs = () => {
|
||||
rowClassName={(job) => (job.enabled ? undefined : 'is-disabled')}
|
||||
footer={(
|
||||
<DataTableFooter
|
||||
shown={shown.length}
|
||||
shown={chrome.shownCount}
|
||||
total={jobs.length}
|
||||
noun="job"
|
||||
/>
|
||||
|
||||
@@ -268,7 +268,7 @@ export default function DeliveryLog() {
|
||||
<GridViewPicker
|
||||
views={chrome.views}
|
||||
label="deliveries"
|
||||
total={`${deliveries.length} of ${stats?.total ?? deliveries.length} deliveries`}
|
||||
total={`${chrome.shownCount} of ${stats?.total ?? deliveries.length} deliveries`}
|
||||
onCreate={chrome.createView}
|
||||
/>
|
||||
<ListToolbar
|
||||
|
||||
@@ -391,7 +391,7 @@ const FleetProxy = () => {
|
||||
<GridViewPicker
|
||||
views={chrome.views}
|
||||
label="servers"
|
||||
total={loading ? null : `${rows.length} server${rows.length === 1 ? '' : 's'}`}
|
||||
total={loading ? null : `${chrome.shownCount} of ${rows.length} server${rows.length === 1 ? '' : 's'}`}
|
||||
onCreate={chrome.createView}
|
||||
/>
|
||||
<ListToolbar
|
||||
|
||||
@@ -395,7 +395,7 @@ export default function Incidents() {
|
||||
<GridViewPicker
|
||||
views={chrome.views}
|
||||
label="incidents"
|
||||
total={`${shown.length} of ${items.length} · ${openIncidents} open · ${activeAlerts.length} host alerts firing`}
|
||||
total={`${chrome.shownCount} of ${items.length} · ${openIncidents} open · ${activeAlerts.length} host alerts firing`}
|
||||
onCreate={chrome.createView}
|
||||
actions={(
|
||||
<>
|
||||
|
||||
@@ -489,8 +489,8 @@ export default function Jobs() {
|
||||
views={chrome.views}
|
||||
label="jobs"
|
||||
total={hasFilters
|
||||
? `${jobs.length} of ${total} matching · ${allJobs} jobs total`
|
||||
: `${jobs.length} of ${allJobs} jobs`}
|
||||
? `${chrome.shownCount} of ${total} matching · ${allJobs} jobs total`
|
||||
: `${chrome.shownCount} of ${allJobs} jobs`}
|
||||
onCreate={chrome.createView}
|
||||
/>
|
||||
<ListToolbar
|
||||
|
||||
@@ -461,7 +461,7 @@ export default function Monitors() {
|
||||
label="monitors"
|
||||
total={(
|
||||
<>
|
||||
{monitors.length} monitor{monitors.length === 1 ? '' : 's'}
|
||||
{chrome.shownCount} of {monitors.length} monitor{monitors.length === 1 ? '' : 's'}
|
||||
{stats?.overall_uptime_30d != null && (
|
||||
<> · {stats.overall_uptime_30d}% uptime (30d)</>
|
||||
)}
|
||||
|
||||
@@ -389,7 +389,7 @@ const QueueDetail = () => {
|
||||
<GridViewPicker
|
||||
views={chrome.views}
|
||||
label="messages"
|
||||
total={`${messages.length} loaded`}
|
||||
total={`${chrome.shownCount} of ${messages.length} loaded`}
|
||||
onCreate={chrome.createView}
|
||||
/>
|
||||
|
||||
|
||||
@@ -565,7 +565,7 @@ const QueueOperations = () => {
|
||||
<GridViewPicker
|
||||
views={chrome.views}
|
||||
label="queues"
|
||||
total={`${filteredQueues.length} of ${queues.length} queues`}
|
||||
total={`${chrome.shownCount} of ${queues.length} queues`}
|
||||
onCreate={chrome.createView}
|
||||
/>
|
||||
|
||||
|
||||
@@ -371,7 +371,7 @@ const Servers = () => {
|
||||
<GridViewPicker
|
||||
views={chrome.views}
|
||||
label="servers"
|
||||
total={`${filteredServers.length} of ${servers.length} servers`}
|
||||
total={`${chrome.shownCount} of ${servers.length} servers`}
|
||||
onCreate={chrome.createView}
|
||||
/>
|
||||
<ListToolbar
|
||||
@@ -446,7 +446,7 @@ const Servers = () => {
|
||||
tableClassName="servers-table"
|
||||
footer={(
|
||||
<DataTableFooter
|
||||
shown={filteredServers.length}
|
||||
shown={chrome.shownCount}
|
||||
total={servers.length}
|
||||
noun="server"
|
||||
/>
|
||||
|
||||
@@ -377,7 +377,7 @@ export default function Telemetry() {
|
||||
<GridViewPicker
|
||||
views={chrome.views}
|
||||
label="events"
|
||||
total={`${events.length} loaded · ${stats?.total || 0} in the last 24h`}
|
||||
total={`${chrome.shownCount} of ${events.length} loaded · ${stats?.total || 0} in the last 24h`}
|
||||
onCreate={chrome.createView}
|
||||
// The "..." sits on the view-name line rather than in a toolbar
|
||||
// of its own -- with nothing else to put there, that toolbar was
|
||||
|
||||
Reference in new issue
Block a user