RepoList.tsx
Raw
1import type { RepositoryRow } from "../../db/index.ts";
2import { formatDate } from "../../lib/formatDate.ts";
3import type { SessionUser } from "../../middleware/session.ts";
4import { Layout } from "../layout.tsx";
5import { Pagination, type PaginationInfo } from "../Pagination.tsx";
6
7interface RepoListProps {
8 user: SessionUser | null;
9 repos: RepositoryRow[];
10 search?: string;
11 pagination: PaginationInfo;
12}
13
14export function RepoList({ user, repos, search, pagination }: RepoListProps) {
15 return (
16 <Layout user={user} title="Repositories">
17 <div class="container">
18 <div class="page-header">
19 <h1 class="page-title">Repositories</h1>
20 {user?.isAdmin && (
21 <a href="/new" class="btn btn-primary">
22 New repository
23 </a>
24 )}
25 </div>
26 <form method="GET" action="/" class="search-form">
27 <div class="search-input-wrap">
28 <input
29 type="search"
30 name="q"
31 value={search ?? ""}
32 placeholder="Search repositories…"
33 class="search-input"
34 autocomplete="off"
35 />
36 <button type="submit" class="btn btn-ghost btn-sm">
37 Search
38 </button>
39 </div>
40 </form>
41 {repos.length === 0 ? (
42 <div class="empty-state">
43 {search ? (
44 <p>
45 No repositories match <strong>{search}</strong>.
46 </p>
47 ) : (
48 <p>No repositories yet.</p>
49 )}
50 </div>
51 ) : (
52 <ul class="repo-list">
53 {repos.map((repo) => (
54 <li class="repo-card">
55 <div class="repo-card-main">
56 <div class="repo-card-title">
57 <a
58 href={`/${repo.name}`}
59 class="repo-name"
60 >
61 {repo.name}
62 </a>
63 {repo.is_private ? (
64 <span class="badge badge-private">
65 Private
66 </span>
67 ) : null}
68 </div>
69 {repo.description && (
70 <p class="repo-description">
71 {repo.description}
72 </p>
73 )}
74 </div>
75 <div class="repo-card-meta">
76 <time
77 class="repo-date"
78 datetime={repo.created_at}
79 >
80 {formatDate(repo.created_at)}
81 </time>
82 </div>
83 </li>
84 ))}
85 </ul>
86 )}
87 <Pagination {...pagination} />
88 </div>
89 </Layout>
90 );
91}
92