68 lines · 2.2 KB
import { reviewDeployment } from "~/fns/deployments";
import { useAction } from "~/hooks/use-action";
import { Button } from "~/ui/Button";
import { ErrorBanner } from "~/ui/Callout";
import { Callout } from "~/ui/Callout";

type PendingDeployment = {
  id: string;
  environment: string;
  waitUntil: number | null;
  needsReview: boolean;
  canReview: boolean;
};

/** Banner for jobs held by environment protection rules, with approve/reject for required reviewers. */
export function DeploymentGate({
  owner,
  repo,
  deployments,
}: {
  owner: string;
  repo: string;
  deployments: PendingDeployment[];
}) {
  const review = useAction(reviewDeployment);
  if (deployments.length === 0) return null;
  return (
    <Callout tone="warning" className="space-y-2 px-4 py-3">
      <ErrorBanner message={review.error} />
      {deployments.map((deployment) => (
        <div key={deployment.id} className="flex flex-wrap items-center gap-3">
          <span>
            Deployment to <span className="font-medium text-warning-100">{deployment.environment}</span>{" "}
            {deployment.needsReview ? "is waiting for a required reviewer" : "is waiting"}
            {deployment.waitUntil && deployment.waitUntil > Date.now() && (
              <>
                {" "}
                · wait timer ends at{" "}
                <time suppressHydrationWarning>{new Date(deployment.waitUntil).toLocaleTimeString()}</time>
              </>