Vectrum
Sign In
46 lines · 1.8 KB · TypeScript
"use client";

import { useActionState, useState } from "react";
import { Checkbox, Select } from "@unlocalhosted/metalui";
import type { FormState } from "@/app/actions/types";
import { MergeIcon } from "@/components/icons";
import { Alert, Button } from "@/components/ui";

const METHODS = [
  { value: "merge", label: "Create a merge commit" },
  { value: "squash", label: "Squash and merge" },
] as const;

/** The merge button with its method, and whether to delete the head branch afterwards. */
export function MergeForm({
  action,
  headSha,
  mergeable,
  canDeleteBranch,
}: {
  action: (prev: FormState, formData: FormData) => Promise<FormState>;
  headSha: string;
  mergeable: boolean;
  canDeleteBranch: boolean;
}) {
  const [state, formAction, pending] = useActionState(action, undefined);
  const [method, setMethod] = useState<"merge" | "squash">("merge");
  return (
    <form action={formAction} className="flex flex-col gap-3">
      <input type="hidden" name="headSha" value={headSha} />
      <input type="hidden" name="method" value={method} />
      <div className="flex flex-wrap items-center gap-3">
        <Select aria-label="Merge method" options={[...METHODS]} value={method} onValueChange={(v) => setMethod(v as "merge" | "squash")} className="w-56" />
        <Button variant="primary" icon={<MergeIcon />} disabled={!mergeable || pending}>
          {pending ? "Merging…" : method === "squash" ? "Squash and Merge" : "Merge Pull Request"}
        </Button>
        {canDeleteBranch && (
          <label className="flex cursor-pointer items-center gap-2 type-ui text-ink2">
            <Checkbox name="deleteBranch" size="row" /> Delete the branch afterwards
          </label>
        )}
      </div>
      {state?.error && <Alert>{state.error.split("\n").map((line, i) => <div key={i}>{line}</div>)}</Alert>}
    </form>
  );
}