Vectrum
Sign In
87 lines · 3.2 KB · TypeScript
"use client";

import { useActionState, useState } from "react";
import { Fieldset } from "@unlocalhosted/metalui";
import type { FormState } from "@/app/actions/types";
import { RepoChecklist, type RepoOption } from "@/components/repo-checklist";
import { ChoiceCards } from "@/components/settings-fields";
import { PlateBody, PlateFooter } from "@/components/settings-plate";
import { Alert, Button } from "@/components/ui";

export function AuthorizeForm({
  action,
  requested,
  repos,
  destination,
}: {
  action: (prev: FormState, formData: FormData) => Promise<FormState>;
  /** What the application asked for; read-only requests can't be widened here. */
  requested: "read" | "write";
  repos: RepoOption[];
  destination: string;
}) {
  const [state, formAction, pending] = useActionState(action, undefined);
  const [permission, setPermission] = useState(requested);
  const [scope, setScope] = useState<"all" | "selected">("all");

  return (
    <form action={formAction}>
      <PlateBody className="gap-6">
        <Fieldset>
          <Fieldset.Legend>Permissions</Fieldset.Legend>
          {requested === "write" ? (
            <ChoiceCards
              name="permission"
              aria-label="Permissions"
              value={permission}
              onValueChange={setPermission}
              columns={1}
              choices={[
                {
                  value: "write",
                  title: "Read & write",
                  description: "Read code, issues and workflow runs; commit files, create branches and repositories, open and edit issues, run workflows.",
                },
                { value: "read", title: "Read-only", description: "Read code, issues and workflow runs, but change nothing." },
              ]}
            />
          ) : (
            <>
              <input type="hidden" name="permission" value="read" />
              <p className="type-body text-ink2">Read-only: it can read code, issues and workflow runs, but change nothing.</p>
            </>
          )}
        </Fieldset>

        <Fieldset>
          <Fieldset.Legend>Repository access</Fieldset.Legend>
          <ChoiceCards
            name="scope"
            aria-label="Repository access"
            value={scope}
            onValueChange={setScope}
            choices={[
              { value: "all", title: "All repositories", description: "Every repository you can access, including ones you get later." },
              { value: "selected", title: "Only select repositories" },
            ]}
          />
          {scope === "selected" && <RepoChecklist repos={repos} needWrite={permission === "write"} />}
        </Fieldset>

        {state?.error && <Alert>{state.error}</Alert>}
        <p className="type-body text-ink2">
          Authorizing sends you back to <b className="text-ink">{destination}</b>. You can disconnect it any time under Settings → Connected
          apps.
        </p>
      </PlateBody>
      <PlateFooter>
        <Button name="decision" value="deny" disabled={pending}>
          Cancel
        </Button>
        <Button variant="primary" name="decision" value="allow" disabled={pending}>
          {pending ? "Authorizing…" : "Authorize"}
        </Button>
      </PlateFooter>
    </form>
  );
}