1- import { ArrowUturnLeftIcon , BookOpenIcon } from "@heroicons/react/20/solid" ;
2- import { type MetaFunction , Outlet , useLocation , useNavigate , useParams } from "@remix-run/react" ;
1+ import {
2+ ArrowPathIcon ,
3+ ArrowUturnLeftIcon ,
4+ BookOpenIcon ,
5+ NoSymbolIcon ,
6+ } from "@heroicons/react/20/solid" ;
7+ import {
8+ Form ,
9+ type MetaFunction ,
10+ Outlet ,
11+ useLocation ,
12+ useNavigate ,
13+ useNavigation ,
14+ useParams ,
15+ } from "@remix-run/react" ;
316import { type LoaderFunctionArgs } from "@remix-run/server-runtime" ;
417import { CogIcon , GitBranchIcon } from "lucide-react" ;
518import { useEffect } from "react" ;
@@ -15,7 +28,15 @@ import { MainCenteredContainer, PageBody, PageContainer } from "~/components/lay
1528import { Badge } from "~/components/primitives/Badge" ;
1629import { Button , LinkButton } from "~/components/primitives/Buttons" ;
1730import { DateTime } from "~/components/primitives/DateTime" ;
18- import { Dialog , DialogTrigger } from "~/components/primitives/Dialog" ;
31+ import { SpinnerWhite } from "~/components/primitives/Spinner" ;
32+ import {
33+ Dialog ,
34+ DialogDescription ,
35+ DialogContent ,
36+ DialogHeader ,
37+ DialogTrigger ,
38+ DialogFooter ,
39+ } from "~/components/primitives/Dialog" ;
1940import { NavBar , PageAccessories , PageTitle } from "~/components/primitives/PageHeader" ;
2041import { PaginationControls } from "~/components/primitives/Pagination" ;
2142import { Paragraph } from "~/components/primitives/Paragraph" ;
@@ -39,10 +60,6 @@ import {
3960 deploymentStatusDescription ,
4061 deploymentStatuses ,
4162} from "~/components/runs/v3/DeploymentStatus" ;
42- import {
43- PromoteDeploymentDialog ,
44- RollbackDeploymentDialog ,
45- } from "~/components/runs/v3/RollbackDeploymentDialog" ;
4663import { useEnvironment } from "~/hooks/useEnvironment" ;
4764import { useOrganization } from "~/hooks/useOrganizations" ;
4865import { useProject } from "~/hooks/useProject" ;
@@ -63,6 +80,7 @@ import { createSearchParams } from "~/utils/searchParams";
6380import { compareDeploymentVersions } from "~/v3/utils/deploymentVersions" ;
6481import { useAutoRevalidate } from "~/hooks/useAutoRevalidate" ;
6582import { env } from "~/env.server" ;
83+ import { DialogClose } from "@radix-ui/react-dialog" ;
6684
6785export const meta : MetaFunction = ( ) => {
6886 return [
@@ -395,7 +413,10 @@ function DeploymentActionsCell({
395413 compareDeploymentVersions ( deployment . version , currentDeployment . version ) === - 1 ;
396414 const canBePromoted = canBeMadeCurrent && ! canBeRolledBack ;
397415
398- if ( ! canBeRolledBack && ! canBePromoted ) {
416+ const finalStatuses = [ "CANCELED" , "DEPLOYED" , "FAILED" , "TIMED_OUT" ] ;
417+ const canBeCanceled = ! finalStatuses . includes ( deployment . status ) ;
418+
419+ if ( ! canBeRolledBack && ! canBePromoted && ! canBeCanceled ) {
399420 return (
400421 < TableCell to = { path } isSelected = { isSelected } >
401422 { "" }
@@ -419,7 +440,7 @@ function DeploymentActionsCell({
419440 fullWidth
420441 textAlignLeft
421442 >
422- Rollback…
443+ Rollback
423444 </ Button >
424445 </ DialogTrigger >
425446 < RollbackDeploymentDialog
@@ -439,7 +460,7 @@ function DeploymentActionsCell({
439460 fullWidth
440461 textAlignLeft
441462 >
442- Promote…
463+ Promote
443464 </ Button >
444465 </ DialogTrigger >
445466 < PromoteDeploymentDialog
@@ -449,8 +470,158 @@ function DeploymentActionsCell({
449470 />
450471 </ Dialog >
451472 ) }
473+ { canBeCanceled && (
474+ < Dialog >
475+ < DialogTrigger asChild >
476+ < Button
477+ variant = "small-menu-item"
478+ LeadingIcon = { NoSymbolIcon }
479+ leadingIconClassName = "text-error"
480+ fullWidth
481+ textAlignLeft
482+ >
483+ Cancel
484+ </ Button >
485+ </ DialogTrigger >
486+ < CancelDeploymentDialog
487+ projectId = { project . id }
488+ deploymentShortCode = { deployment . shortCode }
489+ redirectPath = { `${ location . pathname } ${ location . search } ` }
490+ />
491+ </ Dialog >
492+ ) }
452493 </ >
453494 }
454495 />
455496 ) ;
456497}
498+
499+ type RollbackDeploymentDialogProps = {
500+ projectId : string ;
501+ deploymentShortCode : string ;
502+ redirectPath : string ;
503+ } ;
504+
505+ function RollbackDeploymentDialog ( {
506+ projectId,
507+ deploymentShortCode,
508+ redirectPath,
509+ } : RollbackDeploymentDialogProps ) {
510+ const navigation = useNavigation ( ) ;
511+
512+ const formAction = `/resources/${ projectId } /deployments/${ deploymentShortCode } /rollback` ;
513+ const isLoading = navigation . formAction === formAction ;
514+
515+ return (
516+ < DialogContent key = "rollback" >
517+ < DialogHeader > Rollback to this deployment?</ DialogHeader >
518+ < DialogDescription >
519+ This deployment will become the default for all future runs. Tasks triggered but not
520+ included in this deploy will remain queued until you roll back to or create a new deployment
521+ with these tasks included.
522+ </ DialogDescription >
523+ < DialogFooter >
524+ < DialogClose asChild >
525+ < Button variant = "tertiary/medium" > Cancel</ Button >
526+ </ DialogClose >
527+ < Form
528+ action = { `/resources/${ projectId } /deployments/${ deploymentShortCode } /rollback` }
529+ method = "post"
530+ >
531+ < Button
532+ type = "submit"
533+ name = "redirectUrl"
534+ value = { redirectPath }
535+ variant = "primary/medium"
536+ LeadingIcon = { isLoading ? SpinnerWhite : ArrowPathIcon }
537+ disabled = { isLoading }
538+ shortcut = { { modifiers : [ "mod" ] , key : "enter" } }
539+ >
540+ { isLoading ? "Rolling back..." : "Rollback deployment" }
541+ </ Button >
542+ </ Form >
543+ </ DialogFooter >
544+ </ DialogContent >
545+ ) ;
546+ }
547+
548+ function PromoteDeploymentDialog ( {
549+ projectId,
550+ deploymentShortCode,
551+ redirectPath,
552+ } : RollbackDeploymentDialogProps ) {
553+ const navigation = useNavigation ( ) ;
554+
555+ const formAction = `/resources/${ projectId } /deployments/${ deploymentShortCode } /promote` ;
556+ const isLoading = navigation . formAction === formAction ;
557+
558+ return (
559+ < DialogContent key = "promote" >
560+ < DialogHeader > Promote this deployment?</ DialogHeader >
561+ < DialogDescription >
562+ This deployment will become the default for all future runs not explicitly tied to a
563+ specific deployment.
564+ </ DialogDescription >
565+ < DialogFooter >
566+ < DialogClose asChild >
567+ < Button variant = "tertiary/medium" > Cancel</ Button >
568+ </ DialogClose >
569+ < Form
570+ action = { `/resources/${ projectId } /deployments/${ deploymentShortCode } /promote` }
571+ method = "post"
572+ >
573+ < Button
574+ type = "submit"
575+ name = "redirectUrl"
576+ value = { redirectPath }
577+ variant = "primary/medium"
578+ LeadingIcon = { isLoading ? SpinnerWhite : ArrowPathIcon }
579+ disabled = { isLoading }
580+ shortcut = { { modifiers : [ "mod" ] , key : "enter" } }
581+ >
582+ { isLoading ? "Promoting..." : "Promote deployment" }
583+ </ Button >
584+ </ Form >
585+ </ DialogFooter >
586+ </ DialogContent >
587+ ) ;
588+ }
589+
590+ function CancelDeploymentDialog ( {
591+ projectId,
592+ deploymentShortCode,
593+ redirectPath,
594+ } : RollbackDeploymentDialogProps ) {
595+ const navigation = useNavigation ( ) ;
596+
597+ const formAction = `/resources/${ projectId } /deployments/${ deploymentShortCode } /promote` ;
598+ const isLoading = navigation . formAction === formAction ;
599+
600+ return (
601+ < DialogContent key = "cancel" >
602+ < DialogHeader > Cancel this deployment?</ DialogHeader >
603+ < DialogDescription > Canceling a deployment cannot be undone. Are you sure?</ DialogDescription >
604+ < DialogFooter >
605+ < DialogClose asChild >
606+ < Button variant = "tertiary/medium" > Back</ Button >
607+ </ DialogClose >
608+ < Form
609+ action = { `/resources/${ projectId } /deployments/${ deploymentShortCode } /cancel` }
610+ method = "post"
611+ >
612+ < Button
613+ type = "submit"
614+ name = "redirectUrl"
615+ value = { redirectPath }
616+ variant = "danger/medium"
617+ LeadingIcon = { isLoading ? SpinnerWhite : NoSymbolIcon }
618+ disabled = { isLoading }
619+ shortcut = { { modifiers : [ "mod" ] , key : "enter" } }
620+ >
621+ { isLoading ? "Canceling..." : "Cancel deployment" }
622+ </ Button >
623+ </ Form >
624+ </ DialogFooter >
625+ </ DialogContent >
626+ ) ;
627+ }
0 commit comments