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