Skip to content

Commit

Permalink
fix: Show pending approval button with dialog in deployment releases …
Browse files Browse the repository at this point in the history
…page (#293)
  • Loading branch information
adityachoudhari26 authored Jan 26, 2025
1 parent 2d9043d commit 4edaf3c
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ReleaseStatus } from "@ctrlplane/validators/releases";

import { useReleaseChannelDrawer } from "~/app/[workspaceSlug]/(app)/_components/release-channel-drawer/useReleaseChannelDrawer";
import { api } from "~/trpc/react";
import { ApprovalDialog } from "./[deploymentSlug]/releases/[versionId]/ApprovalCheck";
import { DeployButton } from "./DeployButton";
import { Release } from "./TableCells";

Expand Down Expand Up @@ -48,6 +49,12 @@ const ReleaseEnvironmentCell: React.FC<ReleaseEnvironmentCellProps> = ({
const { data: blockedEnvsResult, isLoading: isBlockedEnvsLoading } =
api.release.blocked.useQuery([release.id]);

const { data: approval, isLoading: isApprovalLoading } =
api.environment.policy.approval.statusByReleasePolicyId.useQuery(
{ releaseId: release.id, policyId: environment.policyId ?? "" },
{ enabled: environment.policyId != null },
);

const blockedEnv = blockedEnvsResult?.find(
(b) => b.environmentId === environment.id,
);
Expand Down Expand Up @@ -85,7 +92,9 @@ const ReleaseEnvironmentCell: React.FC<ReleaseEnvironmentCellProps> = ({
isWorkspaceLoading ||
isStatusesLoading ||
isResourcesLoading ||
isBlockedEnvsLoading;
isBlockedEnvsLoading ||
isApprovalLoading;

if (isLoading)
return <p className="text-xs text-muted-foreground">Loading...</p>;

Expand All @@ -95,19 +104,14 @@ const ReleaseEnvironmentCell: React.FC<ReleaseEnvironmentCellProps> = ({
const hasJobAgent = deployment.jobAgentId != null;
const isBlockedByReleaseChannel = blockedEnv != null;

const isPendingApproval = approval?.status === "pending";

const showBlockedByReleaseChannel =
isBlockedByReleaseChannel &&
!statuses?.some((s) => s.job.status === JobStatus.InProgress);

const isReady = release.status === ReleaseStatus.Ready;

const showRelease = isAlreadyDeployed && !showBlockedByReleaseChannel;
const showDeployButton =
!isAlreadyDeployed &&
hasJobAgent &&
hasResources &&
!isBlockedByReleaseChannel &&
isReady;
const showRelease =
isAlreadyDeployed && !showBlockedByReleaseChannel && !isPendingApproval;

if (showRelease)
return (
Expand All @@ -124,11 +128,6 @@ const ReleaseEnvironmentCell: React.FC<ReleaseEnvironmentCellProps> = ({
/>
);

if (showDeployButton)
return (
<DeployButton releaseId={release.id} environmentId={environment.id} />
);

if (release.status === ReleaseStatus.Building)
return (
<div className="text-center text-xs text-muted-foreground/70">
Expand Down Expand Up @@ -172,11 +171,20 @@ const ReleaseEnvironmentCell: React.FC<ReleaseEnvironmentCellProps> = ({
</div>
);

return (
<div className="text-center text-xs text-muted-foreground/70">
Release not deployed
</div>
);
if (isPendingApproval)
return (
<ApprovalDialog policyId={approval.policyId} release={release}>
<Button
className="w-full border-dashed border-neutral-800/50 bg-transparent text-center text-neutral-800 hover:border-blue-400 hover:bg-transparent hover:text-blue-400"
variant="outline"
size="sm"
>
Pending approval
</Button>
</ApprovalDialog>
);

return <DeployButton releaseId={release.id} environmentId={environment.id} />;
};

export const LazyReleaseEnvironmentCell: React.FC<
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Environment } from "@ctrlplane/db/schema";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { IconLoader2 } from "@tabler/icons-react";

import {
AlertDialog,
Expand All @@ -22,24 +22,34 @@ import { Cancelled, Failing, Loading, Passing, Waiting } from "./StatusIcons";
export const ApprovalDialog: React.FC<{
release: { id: string; version: string };
policyId: string;
linkedEnvironments: Array<Environment>;
children: React.ReactNode;
}> = ({ release, policyId, linkedEnvironments, children }) => {
}> = ({ release, policyId, children }) => {
const policyQ = api.environment.policy.byId.useQuery(policyId);

const [open, setOpen] = useState(false);
const approve = api.environment.policy.approval.approve.useMutation();
const reject = api.environment.policy.approval.reject.useMutation();
const utils = api.useUtils();
const invalidateApproval = () =>
utils.environment.policy.approval.statusByReleasePolicyId.invalidate({
policyId,
releaseId: release.id,
});
const releaseId = release.id;
const onApprove = () =>
approve
.mutateAsync({ releaseId, policyId })
.then(() => router.refresh())
.then(() => invalidateApproval())
.then(() => setOpen(false));
const onReject = () =>
reject
.mutateAsync({ releaseId, policyId })
.then(() => router.refresh())
.then(() => invalidateApproval())
.then(() => setOpen(false));
const router = useRouter();

return (
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogTrigger asChild>{children}</AlertDialogTrigger>
Expand All @@ -48,23 +58,36 @@ export const ApprovalDialog: React.FC<{
<AlertDialogTitle className="text-xl font-semibold">
Approve release <span className="truncate">{release.version}</span>
</AlertDialogTitle>
<AlertDialogDescription>
<div className="flex flex-col gap-2">
Approves this release for the following environments:
<div className="flex flex-wrap gap-2">
{linkedEnvironments.map((env) => (
<Badge key={env.id} variant="secondary" className="max-w-40">
<span className="truncate">{env.name}</span>
</Badge>
))}
{policyQ.isLoading && (
<AlertDialogDescription className="flex items-center justify-center">
<IconLoader2 className="animate-spin" />
</AlertDialogDescription>
)}
{!policyQ.isLoading && (
<AlertDialogDescription>
<div className="flex flex-col gap-2">
Approves this release for the following environments:
<div className="flex flex-wrap gap-2">
{policyQ.data?.environments.map((env) => (
<Badge
key={env.id}
variant="secondary"
className="max-w-40"
>
<span className="truncate">{env.name}</span>
</Badge>
))}
</div>
</div>
</div>
</AlertDialogDescription>
</AlertDialogDescription>
)}
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={onReject}>Reject</AlertDialogCancel>
<AlertDialogAction onClick={onApprove}>Approve</AlertDialogAction>
</AlertDialogFooter>
{!policyQ.isLoading && (
<AlertDialogFooter>
<AlertDialogCancel onClick={onReject}>Reject</AlertDialogCancel>
<AlertDialogAction onClick={onApprove}>Approve</AlertDialogAction>
</AlertDialogFooter>
)}
</AlertDialogContent>
</AlertDialog>
);
Expand All @@ -73,8 +96,7 @@ export const ApprovalDialog: React.FC<{
export const ApprovalCheck: React.FC<{
policyId: string;
release: { id: string; version: string };
linkedEnvironments: Array<Environment>;
}> = ({ policyId, release, linkedEnvironments }) => {
}> = ({ policyId, release }) => {
const approvalStatus =
api.environment.policy.approval.statusByReleasePolicyId.useQuery({
policyId,
Expand Down Expand Up @@ -117,11 +139,7 @@ export const ApprovalCheck: React.FC<{
</div>

{status === "pending" && (
<ApprovalDialog
policyId={policyId}
release={release}
linkedEnvironments={linkedEnvironments}
>
<ApprovalDialog policyId={policyId} release={release}>
<Button size="sm" className="h-6 px-2 py-1">
Review
</Button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
"use client";

import type {
Environment,
EnvironmentPolicyApproval,
User,
} from "@ctrlplane/db/schema";
import type { EnvironmentPolicyApproval, User } from "@ctrlplane/db/schema";

import { Button } from "@ctrlplane/ui/button";

Expand All @@ -13,21 +9,15 @@ import { ApprovalDialog } from "./ApprovalCheck";
type EnvironmentApprovalRowProps = {
approval: EnvironmentPolicyApproval & { user?: User | null };
release: { id: string; version: string };
linkedEnvironments: Environment[];
};

export const EnvironmentApprovalRow: React.FC<EnvironmentApprovalRowProps> = ({
approval,
release,
linkedEnvironments,
}) => {
if (approval.status === "pending")
return (
<ApprovalDialog
release={release}
policyId={approval.policyId}
linkedEnvironments={linkedEnvironments}
>
<ApprovalDialog release={release} policyId={approval.policyId}>
<Button size="sm" className="h-6">
Review
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ export const FlowDiagram: React.FC<{
policyDeployments: policyDeployments.filter(
(p) => p.policyId === policy.id,
),
linkedEnvironments: envs.filter((e) => e.policyId === policy.id),
label: policy.name,
release,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type {
Environment,
EnvironmentPolicy,
EnvironmentPolicyDeployment,
Release,
Expand All @@ -22,7 +21,6 @@ type PolicyNodeProps = NodeProps<
EnvironmentPolicy & {
release: Release;
policyDeployments: Array<EnvironmentPolicyDeployment>;
linkedEnvironments: Array<Environment>;
}
>;

Expand Down Expand Up @@ -131,11 +129,7 @@ export const PolicyNode: React.FC<PolicyNodeProps> = ({ data }) => {
{!noMinSuccess && <MinSuccessCheck {...data} />}
{!noRollout && <GradualRolloutCheck {...data} />}
{!noApproval && (
<ApprovalCheck
policyId={data.id}
release={data.release}
linkedEnvironments={data.linkedEnvironments}
/>
<ApprovalCheck policyId={data.id} release={data.release} />
)}

{noMinSuccess && noRollout && noApproval && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,6 @@ const CollapsibleTableRow: React.FC<CollapsibleTableRowProps> = ({
Record<string, boolean>
>({});

const environmentPolicyQ = api.environment.policy.byId.useQuery(
environment.policyId ?? "",
{ enabled: environment.policyId != null },
);

const linkedEnvironments = environmentPolicyQ.data?.environments ?? [];

const switchResourceExpandedState = (resourceId: string) =>
setExpandedResources((prev) => {
const newState = { ...prev };
Expand Down Expand Up @@ -153,7 +146,6 @@ const CollapsibleTableRow: React.FC<CollapsibleTableRowProps> = ({
key={approval.id}
approval={approval}
release={release}
linkedEnvironments={linkedEnvironments}
/>
))}
</div>
Expand Down

0 comments on commit 4edaf3c

Please sign in to comment.