blob: 3ae779e33c6373b7edeb2c9df04618161013a3dc [file] [log] [blame]
import { NodeRect } from './node-rect';
import { GithubNode, nodeIsConnectable, nodeLabel, useStateStore, useGithubService } from '@/lib/state';
import { useEffect, useMemo, useState } from 'react';
import { z } from "zod";
import { DeepPartial, EventType, useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { Form, FormControl, FormField, FormItem, FormMessage } from './ui/form';
import { Handle, Position } from "@xyflow/react";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { GitHubRepository } from '../lib/github';
import { useProjectId } from '@/lib/state';
import { Alert, AlertDescription } from './ui/alert';
import { AlertCircle } from 'lucide-react';
export function NodeGithub(node: GithubNode) {
const { id, selected } = node;
const isConnectable = useMemo(() => nodeIsConnectable(node, "repository"), [node]);
return (
<NodeRect id={id} selected={selected} type={node.type} state={node.data.state}>
<div style={{ padding: '10px 20px' }}>
{nodeLabel(node)}
<Handle
id="repository"
type={"source"}
position={Position.Right}
isConnectableStart={isConnectable}
isConnectableEnd={isConnectable}
isConnectable={isConnectable}
/>
</div>
</NodeRect>
);
}
const schema = z.object({
repositoryId: z.number().optional(),
});
export function NodeGithubDetails(node: GithubNode) {
const { id, data } = node;
const store = useStateStore();
const projectId = useProjectId();
const [repos, setRepos] = useState<GitHubRepository[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const githubService = useGithubService();
useEffect(() => {
if (data.repository) {
const { id, sshURL } = data.repository;
setRepos(prevRepos => {
if (!prevRepos.some(r => r.id === id)) {
return [...prevRepos, {
id,
name: sshURL.split('/').pop() || '',
full_name: sshURL.split('/').slice(-2).join('/'),
html_url: '',
ssh_url: sshURL,
description: null,
private: false,
default_branch: 'main'
}];
}
return prevRepos;
});
}
}, [data.repository]);
const form = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
mode: "onChange",
defaultValues: {
repositoryId: data.repository?.id,
}
});
useEffect(() => {
const sub = form.watch((value: DeepPartial<z.infer<typeof schema>>, { name, type }: { name?: keyof z.infer<typeof schema> | undefined, type?: EventType | undefined }) => {
if (type !== "change") {
return;
}
switch (name) {
case "repositoryId":
if (value.repositoryId) {
const repo = repos.find(r => r.id === value.repositoryId);
if (repo) {
store.updateNodeData<"github">(id, {
repository: {
id: repo.id,
sshURL: repo.ssh_url,
},
});
}
}
break;
}
});
return () => sub.unsubscribe();
}, [form, store, id, repos]);
useEffect(() => {
const fetchRepositories = async () => {
if (!!!githubService) return;
setLoading(true);
setError(null);
try {
const repositories = await githubService.getRepositories();
setRepos(repositories);
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to fetch repositories");
} finally {
setLoading(false);
}
};
fetchRepositories();
}, [githubService]);
return (
<>
<Form {...form}>
<form className="space-y-2">
<FormField
control={form.control}
name="repositoryId"
render={({ field }) => (
<FormItem>
<Select
onValueChange={(value) => field.onChange(Number(value))}
value={field.value?.toString()}
disabled={loading || !projectId || !!!githubService}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={!!githubService ? "Select a repository" : "GitHub not configured"} />
</SelectTrigger>
</FormControl>
<SelectContent>
{repos.map((repo) => (
<SelectItem key={repo.id} value={repo.id.toString()}>
{repo.full_name}
{repo.description && ` - ${repo.description}`}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
{error && <p className="text-sm text-red-500">{error}</p>}
{loading && <p className="text-sm text-gray-500">Loading repositories...</p>}
{!!!githubService && (
<Alert variant="destructive" className="mt-2">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
GitHub access token is not configured. Please configure it in the Integrations tab.
</AlertDescription>
</Alert>
)}
</FormItem>
)}
/>
</form>
</Form>
</>);
}