Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Approval Usability #172

Merged
merged 11 commits into from
Oct 26, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ import { api } from "~/trpc/react";
const ApprovalDialog: React.FC<{
releaseId: string;
policyId: string;
userId: string;
children: React.ReactNode;
}> = ({ releaseId, policyId, children }) => {
}> = ({ releaseId, policyId, userId, children }) => {
jsbroks marked this conversation as resolved.
Show resolved Hide resolved
const approve = api.environment.policy.approval.approve.useMutation();
const rejected = api.environment.policy.approval.reject.useMutation();
const router = useRouter();
Expand All @@ -56,15 +57,15 @@ const ApprovalDialog: React.FC<{
<AlertDialogFooter>
<AlertDialogCancel
onClick={async () => {
await rejected.mutateAsync({ releaseId, policyId });
await rejected.mutateAsync({ releaseId, policyId, userId });
jsbroks marked this conversation as resolved.
Show resolved Hide resolved
router.refresh();
}}
>
Reject
</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await approve.mutateAsync({ releaseId, policyId });
await approve.mutateAsync({ releaseId, policyId, userId });
router.refresh();
}}
>
Expand Down Expand Up @@ -242,7 +243,11 @@ const ApprovalCheck: React.FC<PolicyNodeProps["data"]> = ({ id, release }) => {
}
const status = approval.data?.status;
return (
<ApprovalDialog policyId={id} releaseId={release.id}>
<ApprovalDialog
policyId={id}
releaseId={release.id}
userId={approval.data?.userId ?? ""}
>
jsbroks marked this conversation as resolved.
Show resolved Hide resolved
<button
disabled={status === "approved" || status === "rejected"}
className="flex w-full items-center gap-2 rounded-md hover:bg-neutral-800/50"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import type {
Environment,
EnvironmentPolicyApproval,
User,
} from "@ctrlplane/db/schema";
import { useRouter } from "next/navigation";

Expand All @@ -12,26 +13,33 @@ import { toast } from "@ctrlplane/ui/toast";
import { api } from "~/trpc/react";

type PolicyApprovalRowProps = {
approval: EnvironmentPolicyApproval;
environments: Environment[];
approval: EnvironmentPolicyApproval & { user?: User };
environment: Environment | undefined;
};

export const PolicyApprovalRow: React.FC<PolicyApprovalRowProps> = ({
approval,
environments,
environment,
}) => {
const router = useRouter();
const utils = api.useUtils();

const { releaseId, policyId } = approval;
if (!environment) {
console.error("Environment is undefined for approval:", approval);
return null;
}

const environmentName = environment.name;
const { releaseId, policyId, status } = approval;
const currentUserID = api.user.viewer.useQuery().data?.id;

const rejectMutation = api.environment.policy.approval.reject.useMutation({
onSuccess: ({ cancelledJobCount }) => {
router.refresh();
utils.environment.policy.invalidate();
utils.job.config.invalidate();
toast.success(
`Rejected release to ${environmentNames} and cancelled ${cancelledJobCount} job${cancelledJobCount !== 1 ? "s" : ""}`,
`Rejected release to ${environmentName} and cancelled ${cancelledJobCount} job${cancelledJobCount !== 1 ? "s" : ""}`,
);
},
onError: () => toast.error("Error rejecting release"),
Expand All @@ -42,28 +50,67 @@ export const PolicyApprovalRow: React.FC<PolicyApprovalRowProps> = ({
router.refresh();
utils.environment.policy.invalidate();
utils.job.config.invalidate();
toast.success(`Approved release to ${environmentNames}`);
toast.success(`Approved release to ${environmentName}`);
},
onError: () => toast.error("Error approving release"),
});

const environmentNames = environments.map((e) => e.name).join(", ");
const handleReject = () => rejectMutation.mutate({ releaseId, policyId });
const handleApprove = () => approveMutation.mutate(approval);
const handleReject = () =>
rejectMutation.mutate({
releaseId,
policyId,
userId: currentUserID!,
zacharyblasczyk marked this conversation as resolved.
Show resolved Hide resolved
});
const handleApprove = () =>
approveMutation.mutate({
releaseId,
policyId,
userId: currentUserID!,
});
zacharyblasczyk marked this conversation as resolved.
Show resolved Hide resolved

return (
<div className="flex items-center gap-2 rounded-md border border-blue-400/50 bg-blue-500/10 p-2 text-sm">
const renderStatusContent = () => {
if (status === "pending") {
return (
<>
<div className="flex items-center gap-2">
<Button
variant="destructive"
className="h-6 px-2"
onClick={handleReject}
>
Reject
</Button>
<Button className="h-6 px-2" onClick={handleApprove}>
Approve
</Button>
</div>
</>
);
}

if (status === "approved")
return (
<div className="ml-2 flex-grow">
<span className="font-medium">
<span className="text-green-300">Approved</span> by{" "}
{approval.user?.name}
</span>
</div>
);
zacharyblasczyk marked this conversation as resolved.
Show resolved Hide resolved

return (
<div className="ml-2 flex-grow">
Approve deploying to {environmentNames}
</div>
<div className="flex shrink-0 items-center gap-2">
<Button variant="secondary" size="sm" onClick={handleReject}>
Reject
</Button>
<Button size="sm" onClick={handleApprove}>
Approve
</Button>
<span className="font-medium">
<span className="text-red-300">Rejected</span> by{" "}
{approval.user?.name}
</span>
</div>
);
};

return (
<div className="flex items-center gap-2 rounded-md text-sm">
{renderStatusContent()}
</div>
);
};
Loading
Loading