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