import type { RepoAppStatus, Repository } from "@vtit-agent-coding/shared"; import { Github } from "lucide-react"; import { useState } from "react"; import { formatRelative } from "../components/TaskDetailFields"; import { Button } from "../components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "../components/ui/dialog"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "../components/ui/tabs"; import { useGithubAppConfig, useInstallableRepos } from "../hooks/useGithubApp"; import { useCreateRepository, useDeleteRepository, useRepositories } from "../hooks/useRepositories"; const STATUS_BADGE: Record = { covered: { label: "App connected", className: "bg-success/10 text-success" }, not_covered: { label: "Not in App", className: "bg-warning/10 text-warning" }, suspended: { label: "App suspended", className: "bg-warning/10 text-warning" }, app_not_installed: { label: "App not installed", className: "bg-surface-tertiary text-content-tertiary" }, }; function RepoStatusBadge({ status, installUrl }: { status?: RepoAppStatus; installUrl: string | null }) { if (!status) return null; const badge = STATUS_BADGE[status]; const span = {badge.label}; if (status === "covered" || !installUrl) return span; return ( {span} ); } export function RepositoriesPage() { const { repos, loading } = useRepositories(); const config = useGithubAppConfig(); const createRepo = useCreateRepository(); const deleteRepo = useDeleteRepository(); const [showDialog, setShowDialog] = useState(false); const [addTab, setAddTab] = useState("github"); const [newName, setNewName] = useState(""); const [newUrl, setNewUrl] = useState(""); const [provider, setProvider] = useState<"github" | "gitlab">("github"); const [gitlabHost, setGitlabHost] = useState("gitlab.com"); const [accessToken, setAccessToken] = useState(""); const [repoToDelete, setRepoToDelete] = useState(null); const installUrl = config?.install_url ?? null; const installable = useInstallableRepos(showDialog && addTab === "github" && Boolean(config?.configured)); async function handleAddManual() { if (!newName.trim() || !newUrl.trim()) return; await createRepo.mutateAsync({ name: newName.trim(), url: newUrl.trim(), provider, gitlab_host: provider === "gitlab" ? gitlabHost.trim() || "gitlab.com" : undefined, access_token: provider === "gitlab" ? accessToken.trim() : undefined, }); setNewName(""); setNewUrl(""); setProvider("github"); setGitlabHost("gitlab.com"); setAccessToken(""); setShowDialog(false); } async function handleImport(name: string, url: string) { await createRepo.mutateAsync({ name, url }); await installable.refetch(); } async function handleDelete() { if (!repoToDelete) return; await deleteRepo.mutateAsync(repoToDelete.id); setRepoToDelete(null); } return (

Repositories

{repos.length} total {config?.configured && installUrl && ( {config.installed ? ( <> Connected {config.accounts[0] && @{config.accounts.join(", @")}} ) : ( "Install GitHub App" )} )}
{loading ? (
{[0, 1, 2].map((i) => (
))}
) : repos.length === 0 ? (

No repositories registered.

) : (
{repos.map((repo) => (
{repo.name} {repo.provider === "gitlab" ? ( GitLab ) : ( )} {repo.url}
Tasks: {repo.task_count ?? 0}
Added: {formatRelative(repo.created_at)}
{repo.url}
))}
)}
{ if (!open) setShowDialog(false); }} > Add Repository Add a repository to track tasks setAddTab(v as string)}> From GitHub Manual {!config?.configured ? (

The GitHub App is not configured on this instance.

) : installable.data?.installed === false ? (

Install the GitHub App to browse and import your repositories.

{installUrl && ( Install GitHub App )}
) : installable.isLoading ? (

Loading repositories…

) : (
{installable.data?.repositories.map((repo) => (
{repo.full_name}
))}
{installUrl && ( Don't see a repository? Configure the App on GitHub → )}
)}
setNewName(e.target.value)} placeholder="my-repo" className="w-full bg-surface-primary border border-border rounded-lg px-3 py-2 text-sm text-content-primary placeholder:text-content-tertiary outline-none focus:border-accent font-mono" />
{provider === "gitlab" && ( <>
setGitlabHost(e.target.value)} placeholder="gitlab.com" className="w-full bg-surface-primary border border-border rounded-lg px-3 py-2 text-sm text-content-primary outline-none focus:border-accent font-mono" />
setAccessToken(e.target.value)} placeholder="glpat-..." className="w-full bg-surface-primary border border-border rounded-lg px-3 py-2 text-sm text-content-primary outline-none focus:border-accent font-mono" />
)}
setNewUrl(e.target.value)} placeholder={provider === "gitlab" ? "https://gitlab.com/user/repo.git" : "https://github.com/user/repo.git"} className="w-full bg-surface-primary border border-border rounded-lg px-3 py-2 text-sm text-content-primary placeholder:text-content-tertiary outline-none focus:border-accent font-mono" />

If the GitHub App isn't installed on this repo, you'll be prompted to install it after adding.

!open && setRepoToDelete(null)}> Remove Repository Remove {repoToDelete?.name} from this workspace. Existing tasks linked to this repository will lose their repository association.
); }