RepoPickerPage.tsx

 1// Repository picker page (/). Auto-redirects when there is exactly one repo.
 2// Shows a list when multiple repos are registered.
 3
 4import { GitFork, FolderOpen, AlertCircle } from "lucide-react";
 5import { useEffect } from "react";
 6import { Link, useNavigate } from "react-router-dom";
 7
 8import { useRepositoriesQuery } from "@/__generated__/graphql";
 9import { Skeleton } from "@/components/ui/skeleton";
10
11function repoSlug(name: string | null | undefined): string {
12  return name ?? "_";
13}
14
15function repoLabel(name: string | null | undefined): string {
16  return name ?? "default";
17}
18
19export function RepoPickerPage() {
20  const { data, loading, error } = useRepositoriesQuery();
21  const navigate = useNavigate();
22
23  // Auto-redirect when there is exactly one repo — no need to pick.
24  useEffect(() => {
25    if (data?.repositories.nodes.length === 1) {
26      navigate("/" + repoSlug(data.repositories.nodes[0].name), { replace: true });
27    }
28  }, [data, navigate]);
29
30  return (
31    <div className="mx-auto max-w-lg py-12">
32      <div className="mb-8 flex items-center gap-3">
33        <GitFork className="size-6 text-muted-foreground" />
34        <h1 className="text-xl font-semibold">Repositories</h1>
35      </div>
36
37      {error && (
38        <div className="flex items-center gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-4 py-3 text-sm text-destructive">
39          <AlertCircle className="size-4 shrink-0" />
40          Failed to load repositories: {error.message}
41        </div>
42      )}
43
44      {loading && !data && (
45        <div className="space-y-2">
46          {Array.from({ length: 3 }).map((_, i) => (
47            <Skeleton key={i} className="h-16 w-full rounded-md" />
48          ))}
49        </div>
50      )}
51
52      <div className="divide-y divide-border rounded-md border border-border">
53        {data?.repositories.nodes.map((repo) => (
54          <Link
55            key={repoSlug(repo.name)}
56            to={`/${repoSlug(repo.name)}`}
57            className="flex items-center gap-3 px-4 py-4 transition-colors hover:bg-muted/50"
58          >
59            <FolderOpen className="size-5 shrink-0 text-muted-foreground" />
60            <p className="font-medium text-foreground">{repoLabel(repo.name)}</p>
61          </Link>
62        ))}
63
64        {data?.repositories.totalCount === 0 && (
65          <p className="px-4 py-8 text-center text-sm text-muted-foreground">
66            No repositories found.
67          </p>
68        )}
69      </div>
70    </div>
71  );
72}