diff --git a/apps/webapp/app/components/layout/MetricsLayout.tsx b/apps/webapp/app/components/layout/MetricsLayout.tsx
index ccf5129b58a..0745e56202b 100644
--- a/apps/webapp/app/components/layout/MetricsLayout.tsx
+++ b/apps/webapp/app/components/layout/MetricsLayout.tsx
@@ -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
*
*
- * …search + TimeFilter…
- *
+ * …TimeFilter…
+ * …page-wide actions…
*
* …stat tiles…
* …chart tiles…
- * …table…
+ * …search…>}>
+ * …table…
+ *
*
* ```
*/
@@ -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
{children}
;
+ const content = {children}
;
+
+ if (!toolbar) {
+ return content;
+ }
+
+ return (
+
+
+ {toolbar}
+
+ {content}
+
+ );
}
export const MetricsLayout = {
diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx
index 4b5673c6f0e..05ac0e7b47d 100644
--- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx
+++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx
@@ -493,12 +493,10 @@ function QueuesWithMetricsView() {
- {/* 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. */}
-
-
-
+
+
{environment.runsEnabled &&
env.pauseSource !== ENVIRONMENT_PAUSE_SOURCE_BILLING_LIMIT ? (
) : null}
-
@@ -688,7 +682,22 @@ function QueuesWithMetricsView() {
) : null}
-
+
+
+
+ >
+ }
+ >
{/* Default overflow-x-auto container so wide tables still scroll horizontally on
narrow viewports; the page (not this region) owns vertical scrolling. */}