Files
kandev/apps/web/components/github/github-app-import-fields.tsx
T
2026-09-10 18:14:05 +01:00

127 lines
3.9 KiB
TypeScript

import { Input } from "@kandev/ui/input";
import { Label } from "@kandev/ui/label";
import { RadioGroup, RadioGroupItem } from "@kandev/ui/radio-group";
import { Textarea } from "@kandev/ui/textarea";
import type { GitHubAppOwnerType, GitHubAppVisibility } from "@/lib/types/github";
import type { AppSetupErrors } from "./github-app-onboarding-model";
import { useTranslation } from "react-i18next";
export type GitHubAppImportValues = {
displayName: string;
ownerLogin: string;
ownerType: GitHubAppOwnerType;
publicBaseUrl: string;
visibility: GitHubAppVisibility;
appId: string;
clientId: string;
clientSecret: string;
privateKey: string;
webhookSecret: string;
slug: string;
};
type Update = (name: keyof GitHubAppImportValues, value: string) => void;
export function GitHubAppImportIdentityFields({
values,
errors,
update,
}: {
values: GitHubAppImportValues;
errors: AppSetupErrors;
update: Update;
}) {
const { t } = useTranslation();
return (
<div className="space-y-3">
<div className="grid gap-3 sm:grid-cols-2">
<Field label={t("github:nameInKandev")} error={errors.displayName}>
<Input
value={values.displayName}
onChange={(e) => update("displayName", e.target.value)}
/>
</Field>
<Field label={t("github:githubAppSlug")} error={errors.slug}>
<Input value={values.slug} onChange={(e) => update("slug", e.target.value)} />
</Field>
<Field label={t("github:githubOwnerLogin")} error={errors.ownerLogin}>
<Input value={values.ownerLogin} onChange={(e) => update("ownerLogin", e.target.value)} />
</Field>
</div>
<RadioGroup
value={values.ownerType}
onValueChange={(value) => update("ownerType", value)}
className="flex min-h-11 flex-wrap items-center gap-5"
>
<Label className="flex cursor-pointer items-center gap-2">
<RadioGroupItem value="Organization" /> {t("github:organization")}
</Label>
<Label className="flex cursor-pointer items-center gap-2">
<RadioGroupItem value="User" /> {t("github:personalAccount")}
</Label>
</RadioGroup>
</div>
);
}
export function GitHubAppImportSecretFields({
values,
errors,
update,
}: {
values: GitHubAppImportValues;
errors: AppSetupErrors;
update: Update;
}) {
const { t } = useTranslation();
// Keys are the value/error field names; only the labels are copy. Resolved
// here rather than at module scope so they follow a locale switch.
const labels = {
appId: t("github:appId"),
clientId: t("github:clientId"),
clientSecret: t("github:clientSecret"),
webhookSecret: t("github:webhookSecret"),
} as const;
return (
<div className="grid gap-3 sm:grid-cols-2">
{(Object.keys(labels) as (keyof typeof labels)[]).map((name) => (
<Field key={name} label={labels[name]} error={errors[name]}>
<Input
type={name.includes("Secret") || name === "webhookSecret" ? "password" : "text"}
autoComplete="off"
value={values[name]}
onChange={(event) => update(name, event.target.value)}
/>
</Field>
))}
<div className="sm:col-span-2">
<Field label={t("github:privateKeyPem", { extension: ".pem" })} error={errors.privateKey}>
<Textarea
className="min-h-28 font-mono text-xs"
value={values.privateKey}
onChange={(event) => update("privateKey", event.target.value)}
/>
</Field>
</div>
</div>
);
}
function Field({
label,
error,
children,
}: {
label: string;
error?: string;
children: React.ReactNode;
}) {
return (
<Label className="block space-y-1.5">
<span>{label}</span>
{children}
{error && <span className="block text-xs font-normal text-destructive">{error}</span>}
</Label>
);
}