@@ -30,7 +30,7 @@ import { Feedback } from "~/components/Feedback";
3030import { PageBody , PageContainer } from "~/components/layout/AppLayout" ;
3131import { BigNumber } from "~/components/metrics/BigNumber" ;
3232import { Badge } from "~/components/primitives/Badge" ;
33- import { Button , LinkButton } from "~/components/primitives/Buttons" ;
33+ import { Button , ButtonVariant , LinkButton } from "~/components/primitives/Buttons" ;
3434import { Callout } from "~/components/primitives/Callout" ;
3535import { Dialog , DialogContent , DialogHeader , DialogTrigger } from "~/components/primitives/Dialog" ;
3636import { FormButtons } from "~/components/primitives/FormButtons" ;
@@ -66,13 +66,14 @@ import { EnvironmentQueuePresenter } from "~/presenters/v3/EnvironmentQueuePrese
6666import { QueueListPresenter } from "~/presenters/v3/QueueListPresenter.server" ;
6767import { requireUserId } from "~/services/session.server" ;
6868import { cn } from "~/utils/cn" ;
69- import { docsPath , EnvironmentParamSchema , v3BillingPath } from "~/utils/pathBuilder" ;
69+ import { docsPath , EnvironmentParamSchema , v3BillingPath , v3RunsPath } from "~/utils/pathBuilder" ;
7070import { PauseEnvironmentService } from "~/v3/services/pauseEnvironment.server" ;
7171import { PauseQueueService } from "~/v3/services/pauseQueue.server" ;
7272import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route" ;
7373import { Header3 } from "~/components/primitives/Headers" ;
7474import { Input } from "~/components/primitives/Input" ;
7575import { useThrottle } from "~/hooks/useThrottle" ;
76+ import { RunsIcon } from "~/assets/icons/RunsIcon" ;
7677
7778const SearchParamsSchema = z . object ( {
7879 query : z . string ( ) . optional ( ) ,
@@ -272,7 +273,20 @@ export default function Page() {
272273 value = { environment . queued }
273274 suffix = { env . paused && environment . queued > 0 ? "paused" : undefined }
274275 animate
275- accessory = { < EnvironmentPauseResumeButton env = { env } /> }
276+ accessory = {
277+ < div className = "flex items-start gap-1" >
278+ < LinkButton
279+ variant = "tertiary/small"
280+ to = { v3RunsPath ( organization , project , env , {
281+ statuses : [ "PENDING" ] ,
282+ period : "30d" ,
283+ } ) }
284+ >
285+ View runs
286+ </ LinkButton >
287+ < EnvironmentPauseResumeButton env = { env } />
288+ </ div >
289+ }
276290 valueClassName = { env . paused ? "text-warning" : undefined }
277291 compactThreshold = { 1000000 }
278292 />
@@ -291,6 +305,17 @@ export default function Page() {
291305 "At concurrency limit"
292306 ) : undefined
293307 }
308+ accessory = {
309+ < LinkButton
310+ variant = "tertiary/small"
311+ to = { v3RunsPath ( organization , project , env , {
312+ statuses : [ "DEQUEUED" , "EXECUTING" ] ,
313+ period : "30d" ,
314+ } ) }
315+ >
316+ View runs
317+ </ LinkButton >
318+ }
294319 compactThreshold = { 1000000 }
295320 />
296321 < BigNumber
@@ -397,6 +422,9 @@ export default function Page() {
397422 queues . map ( ( queue ) => {
398423 const limit = queue . concurrencyLimit ?? environment . concurrencyLimit ;
399424 const isAtLimit = queue . running === limit ;
425+ const queueFilterableName = `${ queue . type === "task" ? "task/" : "" } ${
426+ queue . name
427+ } `;
400428 return (
401429 < TableRow key = { queue . name } >
402430 < TableCell >
@@ -477,6 +505,66 @@ export default function Page() {
477505 hiddenButtons = {
478506 ! queue . paused && < QueuePauseResumeButton queue = { queue } />
479507 }
508+ popoverContent = {
509+ < >
510+ { queue . paused ? (
511+ < QueuePauseResumeButton
512+ queue = { queue }
513+ variant = "minimal/small"
514+ fullWidth
515+ showTooltip = { false }
516+ />
517+ ) : (
518+ < QueuePauseResumeButton
519+ queue = { queue }
520+ variant = "minimal/small"
521+ fullWidth
522+ showTooltip = { false }
523+ />
524+ ) }
525+ < LinkButton
526+ variant = "minimal/small"
527+ to = { v3RunsPath ( organization , project , env , {
528+ queues : [ queueFilterableName ] ,
529+ period : "30d" ,
530+ } ) }
531+ fullWidth
532+ textAlignLeft
533+ LeadingIcon = { RunsIcon }
534+ leadingIconClassName = "text-indigo-500"
535+ >
536+ View all runs
537+ </ LinkButton >
538+ < LinkButton
539+ variant = "minimal/small"
540+ to = { v3RunsPath ( organization , project , env , {
541+ queues : [ queueFilterableName ] ,
542+ statuses : [ "PENDING" ] ,
543+ period : "30d" ,
544+ } ) }
545+ fullWidth
546+ textAlignLeft
547+ LeadingIcon = { RectangleStackIcon }
548+ leadingIconClassName = "text-queues"
549+ >
550+ View queued runs
551+ </ LinkButton >
552+ < LinkButton
553+ variant = "minimal/small"
554+ to = { v3RunsPath ( organization , project , env , {
555+ queues : [ queueFilterableName ] ,
556+ statuses : [ "DEQUEUED" , "EXECUTING" ] ,
557+ period : "30d" ,
558+ } ) }
559+ fullWidth
560+ textAlignLeft
561+ LeadingIcon = { Spinner }
562+ leadingIconClassName = "size-4 animate-none"
563+ >
564+ View running runs
565+ </ LinkButton >
566+ </ >
567+ }
480568 />
481569 </ TableRow >
482570 ) ;
@@ -630,40 +718,56 @@ function EnvironmentPauseResumeButton({
630718
631719function QueuePauseResumeButton ( {
632720 queue,
721+ variant = "tertiary/small" ,
722+ fullWidth = false ,
723+ showTooltip = true ,
633724} : {
634725 /** The "id" here is a friendlyId */
635726 queue : { id : string ; name : string ; paused : boolean } ;
727+ variant ?: ButtonVariant ;
728+ fullWidth ?: boolean ;
729+ showTooltip ?: boolean ;
636730} ) {
637731 const navigation = useNavigation ( ) ;
638732 const [ isOpen , setIsOpen ] = useState ( false ) ;
639733
734+ const button = (
735+ < Button
736+ type = "button"
737+ variant = { variant }
738+ LeadingIcon = { queue . paused ? PlayIcon : PauseIcon }
739+ leadingIconClassName = { queue . paused ? "text-success" : "text-warning" }
740+ fullWidth = { fullWidth }
741+ textAlignLeft = { fullWidth }
742+ >
743+ { queue . paused ? "Resume..." : "Pause..." }
744+ </ Button >
745+ ) ;
746+
747+ const trigger = showTooltip ? (
748+ < div >
749+ < TooltipProvider disableHoverableContent = { true } >
750+ < Tooltip >
751+ < TooltipTrigger asChild >
752+ < div >
753+ < DialogTrigger asChild > { button } </ DialogTrigger >
754+ </ div >
755+ </ TooltipTrigger >
756+ < TooltipContent side = "right" className = { "text-xs" } >
757+ { queue . paused
758+ ? `Resume processing runs in queue "${ queue . name } "`
759+ : `Pause processing runs in queue "${ queue . name } "` }
760+ </ TooltipContent >
761+ </ Tooltip >
762+ </ TooltipProvider >
763+ </ div >
764+ ) : (
765+ < DialogTrigger asChild > { button } </ DialogTrigger >
766+ ) ;
767+
640768 return (
641769 < Dialog open = { isOpen } onOpenChange = { setIsOpen } >
642- < div >
643- < TooltipProvider disableHoverableContent = { true } >
644- < Tooltip >
645- < TooltipTrigger asChild >
646- < div >
647- < DialogTrigger asChild >
648- < Button
649- type = "button"
650- variant = "tertiary/small"
651- LeadingIcon = { queue . paused ? PlayIcon : PauseIcon }
652- leadingIconClassName = { queue . paused ? "text-success" : "text-warning" }
653- >
654- { queue . paused ? "Resume..." : "Pause..." }
655- </ Button >
656- </ DialogTrigger >
657- </ div >
658- </ TooltipTrigger >
659- < TooltipContent side = "right" className = { "text-xs" } >
660- { queue . paused
661- ? `Resume processing runs in queue "${ queue . name } "`
662- : `Pause processing runs in queue "${ queue . name } "` }
663- </ TooltipContent >
664- </ Tooltip >
665- </ TooltipProvider >
666- </ div >
770+ { trigger }
667771 < DialogContent >
668772 < DialogHeader > { queue . paused ? "Resume queue?" : "Pause queue?" } </ DialogHeader >
669773 < div className = "flex flex-col gap-3 pt-3" >
0 commit comments