Skip to content

Commit 8593d74

Browse files
committed
Enable canceling deployments from the dashboard
1 parent 9cc0465 commit 8593d74

File tree

4 files changed

+310
-116
lines changed

4 files changed

+310
-116
lines changed

apps/webapp/app/components/runs/v3/RollbackDeploymentDialog.tsx

Lines changed: 0 additions & 102 deletions
This file was deleted.

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx

Lines changed: 181 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
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";
316
import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
417
import { CogIcon, GitBranchIcon } from "lucide-react";
518
import { useEffect } from "react";
@@ -15,7 +28,15 @@ import { MainCenteredContainer, PageBody, PageContainer } from "~/components/lay
1528
import { Badge } from "~/components/primitives/Badge";
1629
import { Button, LinkButton } from "~/components/primitives/Buttons";
1730
import { 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";
1940
import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader";
2041
import { PaginationControls } from "~/components/primitives/Pagination";
2142
import { 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";
4663
import { useEnvironment } from "~/hooks/useEnvironment";
4764
import { useOrganization } from "~/hooks/useOrganizations";
4865
import { useProject } from "~/hooks/useProject";
@@ -63,6 +80,7 @@ import { createSearchParams } from "~/utils/searchParams";
6380
import { compareDeploymentVersions } from "~/v3/utils/deploymentVersions";
6481
import { useAutoRevalidate } from "~/hooks/useAutoRevalidate";
6582
import { env } from "~/env.server";
83+
import { DialogClose } from "@radix-ui/react-dialog";
6684

6785
export 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

Comments
 (0)