CommitDetail.tsx
Raw
1import { escapeHtml } from "@kitajs/html";
2import type { RepositoryRow } from "../../db/index.ts";
3import { formatDateTime } from "../../lib/formatDate.ts";
4import type { SessionUser } from "../../middleware/session.ts";
5import type { RenderedDiffFile } from "../../services/diffHighlight.ts";
6import type { CommitMeta } from "../../services/git.ts";
7import { DiffView } from "../DiffView.tsx";
8import { Layout } from "../layout.tsx";
9import { RepoHeader } from "../repos/RepoHeader.tsx";
10import { RepoNav } from "./RepoNav.tsx";
11
12interface CommitDetailProps {
13 user: SessionUser | null;
14 repo: RepositoryRow;
15 sha: string;
16 meta: CommitMeta;
17 files: RenderedDiffFile[];
18}
19
20export function CommitDetail({
21 user,
22 repo,
23 sha,
24 meta,
25 files,
26}: CommitDetailProps) {
27 return (
28 <Layout user={user} title={`${sha.slice(0, 7)} — ${repo.name}`}>
29 <div class="container">
30 <RepoHeader repo={repo} />
31 <RepoNav repo={repo} active="commits" user={user} />
32
33 {/* Back link */}
34 <div class="commit-page-top">
35 <a
36 href={`/${repo.name}/commits/${repo.default_branch}`}
37 class="btn btn-ghost btn-sm"
38 >
39 ← Back to log
40 </a>
41 <a
42 href={`/${repo.name}/tree/${sha}`}
43 class="btn btn-secondary btn-sm"
44 title={`Browse tree at ${sha.slice(0, 7)}`}
45 >
46 @ {sha.slice(0, 7)}
47 </a>
48 </div>
49
50 {/* Commit metadata card */}
51 <div class="commit-card">
52 <h2 class="commit-card-subject">{meta.subject}</h2>
53 {meta.body && (
54 <pre class="commit-card-body">{meta.body}</pre>
55 )}
56 <div class="commit-card-meta">
57 <div class="commit-card-meta-row">
58 <span class="commit-meta-label">Author</span>
59 <span class="commit-meta-value">
60 {escapeHtml(`${meta.author} <${meta.email}>`)}
61 </span>
62 </div>
63 <div class="commit-card-meta-row">
64 <span class="commit-meta-label">Date</span>
65 <time
66 class="commit-meta-value"
67 datetime={meta.date}
68 >
69 {formatDateTime(meta.date)}
70 </time>
71 </div>
72 {(meta.committer !== meta.author ||
73 meta.committerEmail !== meta.email) && (
74 <>
75 <div class="commit-card-meta-row">
76 <span class="commit-meta-label">
77 Committer
78 </span>
79 <span class="commit-meta-value">
80 {escapeHtml(
81 `${meta.committer} <${meta.committerEmail}>`,
82 )}
83 </span>
84 </div>
85 <div class="commit-card-meta-row">
86 <span class="commit-meta-label">
87 Commit date
88 </span>
89 <time
90 class="commit-meta-value"
91 datetime={meta.committerDate}
92 >
93 {formatDateTime(meta.committerDate)}
94 </time>
95 </div>
96 </>
97 )}
98 <div class="commit-card-meta-row">
99 <span class="commit-meta-label">Commit</span>
100 <code class="commit-meta-value commit-sha-full mono">
101 {meta.hash}
102 </code>
103 </div>
104 {meta.parents.length > 0 && (
105 <div class="commit-card-meta-row">
106 <span class="commit-meta-label">
107 Parent{meta.parents.length > 1 ? "s" : ""}
108 </span>
109 <span class="commit-meta-value">
110 {meta.parents.map((p) => (
111 <a
112 href={`/${repo.name}/commit/${p}`}
113 class="commit-hash mono"
114 >
115 {p.slice(0, 7)}
116 </a>
117 ))}
118 </span>
119 </div>
120 )}
121 </div>
122 </div>
123
124 <DiffView files={files} repo={repo} sha={sha} />
125 </div>
126 </Layout>
127 );
128}
129