IssueList.tsx
Raw
1import type { IssueRow, RepositoryRow } from "../../db/index.ts";
2import { displayName } from "../../lib/users.ts";
3import type { SessionUser } from "../../middleware/session.ts";
4import { Avatar } from "../Avatar.tsx";
5import { Layout } from "../layout.tsx";
6import { Pagination, type PaginationInfo } from "../Pagination.tsx";
7import { RepoHeader } from "../repos/RepoHeader.tsx";
8import { RepoNav } from "../repos/RepoNav.tsx";
9
10interface IssueListProps {
11 user: SessionUser | null;
12 repo: RepositoryRow;
13 issues: (IssueRow & { author_username: string; author_avatar_version: number | null })[];
14 status: "open" | "closed" | "completed";
15 counts: Record<string, number>;
16 pagination: PaginationInfo;
17}
18
19export function IssueList({
20 user,
21 repo,
22 issues,
23 status,
24 counts,
25 pagination,
26}: IssueListProps) {
27 return (
28 <Layout user={user} title={`Issues — ${repo.name}`}>
29 <div class="container">
30 <RepoHeader repo={repo} />
31 <RepoNav repo={repo} active="issues" user={user} />
32 <div class="list-header">
33 <div class="list-header-tabs">
34 <a
35 href={`/${repo.name}/issues?status=open`}
36 class={`list-tab${status === "open" ? " active" : ""}`}
37 >
38 Open{" "}
39 {(counts.open ?? 0) > 0 && (
40 <span class="tab-count">{counts.open}</span>
41 )}
42 </a>
43 <a
44 href={`/${repo.name}/issues?status=completed`}
45 class={`list-tab${status === "completed" ? " active" : ""}`}
46 >
47 Completed{" "}
48 {(counts.completed ?? 0) > 0 && (
49 <span class="tab-count">
50 {counts.completed}
51 </span>
52 )}
53 </a>
54 <a
55 href={`/${repo.name}/issues?status=closed`}
56 class={`list-tab${status === "closed" ? " active" : ""}`}
57 >
58 Closed{" "}
59 {(counts.closed ?? 0) > 0 && (
60 <span class="tab-count">{counts.closed}</span>
61 )}
62 </a>
63 </div>
64 {user && (
65 <a
66 href={`/${repo.name}/issues/new`}
67 class="btn btn-primary btn-sm"
68 >
69 New issue
70 </a>
71 )}
72 </div>
73 {issues.length === 0 ? (
74 <div class="empty-state">
75 <p>No {status} issues.</p>
76 </div>
77 ) : (
78 <ul class="issue-list">
79 {issues.map((issue) => (
80 <li class="issue-item">
81 <div class="issue-main">
82 <span
83 class={`issue-status-dot ${issue.status}`}
84 />
85 <a
86 href={`/${repo.name}/issues/${issue.number}`}
87 class="issue-title"
88 >
89 {issue.title}
90 </a>
91 <span class="issue-number">
92 #{issue.number}
93 </span>
94 </div>
95 <div class="issue-meta">
96 <Avatar
97 userId={issue.author_id}
98 version={issue.author_avatar_version}
99 size={20}
100 />
101 <span class="issue-author">
102 opened by{" "}
103 {displayName(issue.author_username)}
104 </span>
105 <time datetime={issue.created_at}>
106 {new Date(
107 issue.created_at,
108 ).toLocaleDateString()}
109 </time>
110 </div>
111 </li>
112 ))}
113 </ul>
114 )}
115 <Pagination {...pagination} />
116 </div>
117 </Layout>
118 );
119}
120