4.5 KB131 lines
Blame
1import type { Metadata } from "next";
2import Link from "next/link";
3import { timeAgoFromDate } from "@/lib/utils";
4import { getRepoDiffs } from "@/lib/grove-api";
5
6interface Props {
7 params: Promise<{ owner: string; repo: string }>;
8 searchParams: Promise<{ status?: string }>;
9}
10
11export async function generateMetadata({ params }: Props): Promise<Metadata> {
12 const { repo } = await params;
13 return { title: `Diffs · ${repo}` };
14}
15
16
17const statusStyles: Record<string, { bg: string; text: string; border: string }> = {
18 open: { bg: "var(--status-open-bg)", text: "var(--status-open-text)", border: "var(--status-open-border)" },
19 landed: { bg: "var(--status-merged-bg)", text: "var(--status-merged-text)", border: "var(--status-merged-border)" },
20 closed: { bg: "var(--status-closed-bg)", text: "var(--status-closed-text)", border: "var(--status-closed-border)" },
21};
22
23export default async function DiffsPage({
24 params,
25 searchParams,
26}: Props) {
27 const { owner, repo } = await params;
28 const { status: statusParam } = await searchParams;
29 const status = statusParam ?? "open";
30
31 const data = await getRepoDiffs(owner, repo, status);
32
33 return (
34 <>
35 <div className="flex items-center justify-between mb-4">
36 <div className="flex gap-4 text-sm">
37 {(["open", "landed", "closed"] as const).map((s) => (
38 <Link
39 key={s}
40 href={`/${owner}/${repo}/diffs?status=${s}`}
41 className="capitalize"
42 style={{
43 color: status === s ? "var(--text-primary)" : "var(--text-muted)",
44 fontWeight: status === s ? 600 : 400,
45 }}
46 >
47 {s}
48 </Link>
49 ))}
50 </div>
51 <Link
52 href={`/${owner}/${repo}/diffs/new`}
53 className="text-sm px-3 py-1"
54 style={{
55 backgroundColor: "var(--accent)",
56 color: "var(--accent-text)",
57 }}
58 >
59 New diff
60 </Link>
61 </div>
62
63 {data?.diffs?.length ? (
64 <div style={{ border: "1px solid var(--border-subtle)" }}>
65 <table className="w-full text-sm">
66 <tbody>
67 {data.diffs.map((d: any, i: number) => {
68 const style = statusStyles[d.status ?? status] ?? statusStyles.open;
69 return (
70 <tr
71 key={d.id}
72 className="hover-row"
73 style={{
74 borderTop:
75 i > 0 ? "1px solid var(--divide)" : undefined,
76 }}
77 >
78 <td className="py-2.5 pl-3 pr-3">
79 <div className="flex items-center gap-2">
80 <span
81 className="text-xs px-1.5 py-0.5 shrink-0"
82 style={{
83 backgroundColor: style.bg,
84 color: style.text,
85 border: `1px solid ${style.border}`,
86 }}
87 >
88 {d.status ?? status}
89 </span>
90 <Link
91 href={`/${owner}/${repo}/diffs/${d.number}`}
92 className="hover:underline truncate"
93 style={{ color: "var(--accent)" }}
94 >
95 {d.title}
96 </Link>
97 </div>
98 <div
99 className="text-xs mt-0.5 ml-0"
100 style={{ color: "var(--text-muted)" }}
101 >
102 D{d.number} by {d.author_name}
103 </div>
104 </td>
105 <td
106 className="py-2 text-xs font-mono text-right w-28 hidden sm:table-cell"
107 style={{ color: "var(--text-faint)" }}
108 >
109 {d.head_commit?.slice(0, 8)}
110 </td>
111 <td
112 className="py-2 pr-3 text-xs text-right w-20"
113 style={{ color: "var(--text-faint)" }}
114 >
115 {timeAgoFromDate(d.created_at)}
116 </td>
117 </tr>
118 );
119 })}
120 </tbody>
121 </table>
122 </div>
123 ) : (
124 <p className="text-sm py-8 text-center" style={{ color: "var(--text-faint)" }}>
125 No {status} diffs.
126 </p>
127 )}
128 </>
129 );
130}
131