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