| 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> |
| ); |
| } |