feat(webapp): consolidate the queue detail header into one top bar (TRI-12315, TRI-12316)
Move the Overview/Concurrency-keys tabs up into the filter bar (tabs left, inset by pl-2), with the date filter and the relocated Override limit + Pause {queue} queue buttons on the right (gap-1.5). Both controls move out of the Concurrency panel and gain visible labels; the override button gets an explanatory tooltip. Adds withQueueName to QueuePauseResumeButton and a labeled+tooltip 'button' trigger to QueueOverrideConcurrencyButton.
This commit is contained in:
@@ -32,6 +32,7 @@ export function QueuePauseResumeButton({
|
||||
fullWidth = false,
|
||||
showTooltip = true,
|
||||
iconOnly = false,
|
||||
withQueueName = false,
|
||||
}: {
|
||||
/** The "id" here is a friendlyId */
|
||||
queue: { id: string; name: string; paused: boolean };
|
||||
@@ -41,6 +42,8 @@ export function QueuePauseResumeButton({
|
||||
/** Icon-only trigger (label moves to the tooltip). For compact placements like the detail-page
|
||||
* live blocks. */
|
||||
iconOnly?: boolean;
|
||||
/** Render the full "Pause/Resume {name} queue" label instead of the short "Pause"/"Resume". */
|
||||
withQueueName?: boolean;
|
||||
}) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
@@ -64,7 +67,13 @@ export function QueuePauseResumeButton({
|
||||
textAlignLeft={fullWidth}
|
||||
aria-label={label}
|
||||
>
|
||||
{iconOnly ? undefined : queue.paused ? "Resume" : "Pause"}
|
||||
{iconOnly
|
||||
? undefined
|
||||
: withQueueName
|
||||
? `${queue.paused ? "Resume" : "Pause"} ${queue.name} queue`
|
||||
: queue.paused
|
||||
? "Resume"
|
||||
: "Pause"}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
</div>
|
||||
@@ -209,24 +218,38 @@ export function QueueOverrideConcurrencyButton({
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
) : trigger === "button" ? (
|
||||
<TooltipProvider disableHoverableContent={true}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="cursor-pointer [&_button]:cursor-pointer">
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary/small"
|
||||
LeadingIcon={AdjustmentsHorizontalIcon}
|
||||
leadingIconClassName="text-text-dimmed"
|
||||
aria-label={
|
||||
isOverridden ? "Edit concurrency override" : "Override concurrency limit"
|
||||
}
|
||||
>
|
||||
{isOverridden ? "Edit override" : "Override limit"}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="max-w-[230px] text-xs">
|
||||
Set a custom concurrency limit for this queue, overriding the environment default — as
|
||||
an absolute number or a percentage of the environment limit.
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
) : (
|
||||
<DialogTrigger asChild>
|
||||
{trigger === "button" ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="minimal/small"
|
||||
LeadingIcon={AdjustmentsHorizontalIcon}
|
||||
leadingIconClassName="text-text-dimmed"
|
||||
aria-label={isOverridden ? "Edit concurrency override" : "Override concurrency limit"}
|
||||
>
|
||||
{isOverridden ? "Edit override" : "Override"}
|
||||
</Button>
|
||||
) : (
|
||||
<PopoverMenuItem
|
||||
icon={AdjustmentsHorizontalIcon}
|
||||
title={isOverridden ? "Edit override…" : "Override limit…"}
|
||||
/>
|
||||
)}
|
||||
<PopoverMenuItem
|
||||
icon={AdjustmentsHorizontalIcon}
|
||||
title={isOverridden ? "Edit override…" : "Override limit…"}
|
||||
/>
|
||||
</DialogTrigger>
|
||||
)}
|
||||
<DialogContent>
|
||||
|
||||
+30
-37
@@ -245,7 +245,25 @@ export default function Page() {
|
||||
everything, like the Queues list. The time filter scopes the tab charts; search filters
|
||||
the keys table. The bar is pinned by the layout while the page scrolls. */}
|
||||
<MetricsLayout.Filters>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="pl-2">
|
||||
<TabContainer>
|
||||
<TabButton
|
||||
isActive={view === "overview"}
|
||||
layoutId="queue-detail-view"
|
||||
onClick={() => replace({ view: undefined, key: undefined })}
|
||||
>
|
||||
Overview
|
||||
</TabButton>
|
||||
<TabButton
|
||||
isActive={view === "keys"}
|
||||
layoutId="queue-detail-view"
|
||||
onClick={() => replace({ view: "keys" })}
|
||||
>
|
||||
Concurrency keys
|
||||
</TabButton>
|
||||
</TabContainer>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
{view === "keys" && hasKeys ? (
|
||||
<SearchInput placeholder="Search keys…" paramName="query" resetParams={["key"]} />
|
||||
) : null}
|
||||
@@ -257,6 +275,16 @@ export default function Page() {
|
||||
valueClassName="text-text-bright"
|
||||
shortcut={{ key: "d" }}
|
||||
/>
|
||||
<QueueOverrideConcurrencyButton
|
||||
queue={queue}
|
||||
environmentConcurrencyLimit={environmentConcurrencyLimit}
|
||||
trigger="button"
|
||||
/>
|
||||
<QueuePauseResumeButton
|
||||
queue={{ id: queue.id, name: queue.name, paused: queue.paused }}
|
||||
variant="secondary/small"
|
||||
withQueueName
|
||||
/>
|
||||
</div>
|
||||
</MetricsLayout.Filters>
|
||||
|
||||
@@ -275,23 +303,6 @@ export default function Page() {
|
||||
{/* Tabs + charts share the padded (inset) column. Both tabs always render; the keys tab
|
||||
shows an empty state when the queue has no concurrency keys. */}
|
||||
<MetricsLayout.Content inset>
|
||||
<TabContainer>
|
||||
<TabButton
|
||||
isActive={view === "overview"}
|
||||
layoutId="queue-detail-view"
|
||||
onClick={() => replace({ view: undefined, key: undefined })}
|
||||
>
|
||||
Overview
|
||||
</TabButton>
|
||||
<TabButton
|
||||
isActive={view === "keys"}
|
||||
layoutId="queue-detail-view"
|
||||
onClick={() => replace({ view: "keys" })}
|
||||
>
|
||||
Concurrency keys
|
||||
</TabButton>
|
||||
</TabContainer>
|
||||
|
||||
{view === "keys" ? (
|
||||
hasKeys ? (
|
||||
<ConcurrencyKeyCharts
|
||||
@@ -983,25 +994,7 @@ function QueueStats({
|
||||
|
||||
return (
|
||||
<MetricsLayout.Grid>
|
||||
<ConcurrencyBlock
|
||||
running={runningDisplay}
|
||||
limit={limitDisplay}
|
||||
paused={queue.paused}
|
||||
accessory={
|
||||
<div className="flex items-center gap-1">
|
||||
<QueuePauseResumeButton
|
||||
queue={{ id: queue.id, name: queue.name, paused: queue.paused }}
|
||||
variant="secondary/small-icon"
|
||||
iconOnly
|
||||
/>
|
||||
<QueueOverrideConcurrencyButton
|
||||
queue={queue}
|
||||
environmentConcurrencyLimit={environmentConcurrencyLimit}
|
||||
trigger="icon"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<ConcurrencyBlock running={runningDisplay} limit={limitDisplay} paused={queue.paused} />
|
||||
<BigNumber
|
||||
title="Queued"
|
||||
value={queuedDisplay}
|
||||
|
||||
Reference in New Issue
Block a user