fix(webapp): move Queues search and pagination above the table (#4834)

This commit is contained in:
claude[bot]
2026-08-30 12:36:17 +01:00
committed by GitHub
parent 9cb5028ce1
commit 1d55693c0f
2 changed files with 56 additions and 16 deletions
@@ -9,6 +9,7 @@
* - `Grid` — tiles; columns derived from tile count unless `columns` is set. `kind="charts"`
* bakes the fixed chart-row height.
* - `Content` — table / tabs below the tiles. Full-bleed by default; `inset` for a padded column.
* `toolbar` adds a bar flush above the content for controls that scope only this region.
*
* Optional:
* - `Sidebar` — a persistent right-hand panel; fixed `width` or `resizable`. Present ⇒ Root
@@ -22,12 +23,14 @@
* ```tsx
* <MetricsLayout.Root>
* <MetricsLayout.Filters>
* <div className="flex items-center gap-2">…search + TimeFilter…</div>
* <PaginationControls … />
* <div className="flex items-center gap-2">…TimeFilter…</div>
* <div className="flex items-center gap-2">…page-wide actions…</div>
* </MetricsLayout.Filters>
* <MetricsLayout.Grid>…stat tiles…</MetricsLayout.Grid>
* <MetricsLayout.Grid kind="charts">…chart tiles…</MetricsLayout.Grid>
* <MetricsLayout.Content>…table…</MetricsLayout.Content>
* <MetricsLayout.Content toolbar={<>…search…<PaginationControls … /></>}>
* …table…
* </MetricsLayout.Content>
* </MetricsLayout.Root>
* ```
*/
@@ -341,16 +344,44 @@ function MetricsLayoutGrid({
* spans edge to edge with its own top border; pass `inset` for a padded column (the detail page's
* tabs + charts). Separation from the tiles above comes from the scroll column's gap alone (no
* extra top margin), so the tile → content step matches the gap between tile rows.
*
* Pass `toolbar` for controls that scope this region only (search, pagination) — they sit flush on
* top of the content instead of in the page-wide `Filters` bar, so their scope is visible.
*/
function MetricsLayoutContent({
children,
inset = false,
toolbar,
}: {
children: ReactNode;
/** Pad the content into a column (page gutter) instead of letting it span edge to edge. */
inset?: boolean;
/**
* Controls that act on this region alone. Rendered as a bar directly above the content with no
* gap, so it reads as belonging to the table below rather than to the tiles above. Compose
* left/right clusters as child divs — `justify-between` spreads them.
*/
toolbar?: ReactNode;
}) {
return <div className={cn("flex flex-col gap-2.5", inset && "px-2.5")}>{children}</div>;
const content = <div className={cn("flex flex-col gap-2.5", inset && "px-2.5")}>{children}</div>;
if (!toolbar) {
return content;
}
return (
<div className="flex flex-col">
<div
className={cn(
"flex items-center justify-between gap-2 border-t border-grid-dimmed px-1.5 py-1.5",
inset && "mx-2.5"
)}
>
{toolbar}
</div>
{content}
</div>
);
}
export const MetricsLayout = {
@@ -493,12 +493,10 @@ function QueuesWithMetricsView() {
</PageAccessories>
</NavBar>
<MetricsLayout.Root>
{/* Filters — pinned bar directly under the NavBar. Left cluster = search + period; right
cluster = pagination. */}
{/* Filters — pinned bar directly under the NavBar. This row is page-wide only: Period is
the one control that changes the tiles and charts below, so it leads the row. Search
and pagination scope the table alone and live in that table's own bar instead. */}
<MetricsLayout.Filters className="px-2">
<div className="flex items-center gap-1.5">
<QueueFilters />
</div>
<div className="flex items-center gap-1.5">
<TimeFilter
period={timeRange.period ?? undefined}
@@ -507,16 +505,12 @@ function QueuesWithMetricsView() {
maxPeriodDays={maxPeriodDays}
shortcut={{ key: "d" }}
/>
</div>
<div className="flex items-center gap-1.5">
{environment.runsEnabled &&
env.pauseSource !== ENVIRONMENT_PAUSE_SOURCE_BILLING_LIMIT ? (
<EnvironmentPauseResumeButton env={env} />
) : null}
<PaginationControls
currentPage={pagination.currentPage}
totalPages={pagination.mode === "unfiltered" ? pagination.totalPages : 1}
hasNextPage={pagination.mode === "filtered" ? pagination.hasMore : undefined}
showPageNumbers={false}
/>
</div>
</MetricsLayout.Filters>
@@ -688,7 +682,22 @@ function QueuesWithMetricsView() {
</ChartSyncProvider>
) : null}
<MetricsLayout.Content>
<MetricsLayout.Content
/* Search + pagination only ever affect the table, so they sit on the table rather than
in the page-wide Filters bar, where their position implied they filtered the metrics
above. Same left/right split as the classic view's bar. */
toolbar={
<>
<QueueFilters />
<PaginationControls
currentPage={pagination.currentPage}
totalPages={pagination.mode === "unfiltered" ? pagination.totalPages : 1}
hasNextPage={pagination.mode === "filtered" ? pagination.hasMore : undefined}
showPageNumbers={false}
/>
</>
}
>
{/* Default overflow-x-auto container so wide tables still scroll horizontally on
narrow viewports; the page (not this region) owns vertical scrolling. */}
<Table containerClassName="border-t">