blob: 722c43c6c9b11d64767cf300be094a9a8aa4fd0f [file] [log] [blame]
import { useProjectId, useGithubService, useStateStore } from "@/lib/state";
import { Form, FormControl, FormField, FormItem, FormMessage } from "./components/ui/form";
import { Input } from "./components/ui/input";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "./components/ui/button";
import { useToast } from "@/hooks/use-toast";
import { CircleCheck, CircleX } from "lucide-react";
import { useState, useCallback } from "react";
const schema = z.object({
githubToken: z.string().min(1, "GitHub token is required"),
});
export function Integrations() {
const { toast } = useToast();
const store = useStateStore();
const projectId = useProjectId();
const [isEditing, setIsEditing] = useState(false);
const githubService = useGithubService();
const [isSaving, setIsSaving] = useState(false);
const form = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
mode: "onChange",
});
const onSubmit = useCallback(
async (data: z.infer<typeof schema>) => {
if (!projectId) return;
setIsSaving(true);
try {
const response = await fetch(`/api/project/${projectId}/github-token`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ githubToken: data.githubToken }),
});
if (!response.ok) {
throw new Error("Failed to save GitHub token");
}
await store.refreshEnv();
setIsEditing(false);
form.reset();
toast({
title: "GitHub token saved successfully",
});
} catch (error) {
toast({
variant: "destructive",
title: "Failed to save GitHub token",
description: error instanceof Error ? error.message : "Unknown error",
});
} finally {
setIsSaving(false);
}
},
[projectId, store, form, toast, setIsEditing, setIsSaving],
);
const handleCancel = () => {
setIsEditing(false);
form.reset();
};
return (
<div className="px-4 py-1">
<div className="flex flex-col gap-1">
<div className="flex flex-row items-center gap-1">
{githubService ? <CircleCheck /> : <CircleX />}
<div>Github</div>
</div>
{!!githubService && !isEditing && (
<Button variant="outline" className="w-fit" onClick={() => setIsEditing(true)}>
Update Access Token
</Button>
)}
{(!githubService || isEditing) && (
<div className="flex flex-row items-center gap-1 text-sm">
<div>
Follow the link to generate new PAT:{" "}
<a href="https://github.com/settings/personal-access-tokens" target="_blank">
https://github.com/settings/personal-access-tokens
</a>
<br />
Grant following <b>Repository</b> permissions:
<ul>
<li>
<b>Contents</b> - Read-only access so dodo can clone the repository
</li>
<li>
<b>Metadata</b> - Read-only access so dodo can search for repositories
</li>
<li>
<b>Webhooks</b> - Read and write access so dodo can register webhooks with Github
and receive updates
</li>
<li>
<b>Administration</b> - Read and write access so dodo automatically add deploy keys
to repositories
</li>
</ul>
</div>
</div>
)}
{(!githubService || isEditing) && (
<Form {...form}>
<form className="space-y-2" onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="githubToken"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
type="password"
placeholder="Personal Access Token"
className="w-1/4"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-row items-center gap-1">
<Button type="submit" disabled={isSaving}>
{isSaving ? "Saving..." : "Save"}
</Button>
{!!githubService && (
<Button type="button" variant="outline" onClick={handleCancel} disabled={isSaving}>
Cancel
</Button>
)}
</div>
</form>
</Form>
)}
</div>
</div>
);
}