1import { Link } from "@tanstack/react-router";
2import { formatDistanceToNow } from "date-fns";
3import { MessageSquare, CircleDot, CircleCheck } from "lucide-react";
4
5import { Status } from "@/__generated__/graphql";
6
7import { LabelBadge } from "./LabelBadge";
8
9interface BugRowProps {
10 id: string;
11 humanId: string;
12 status: Status;
13 title: string;
14 labels: Array<{ name: string; color: { R: number; G: number; B: number } }>;
15 author: { humanId: string; displayName: string; avatarUrl?: string | null };
16 createdAt: string;
17 commentCount: number;
18 repo: string;
19 onLabelClick?: (name: string) => void;
20}
21
22// Single row in the issue list. Shows status icon, title, labels, author and
23// comment count. Labels are clickable to filter the list by that label.
24export function BugRow({
25 humanId,
26 status,
27 title,
28 labels,
29 author,
30 createdAt,
31 commentCount,
32 repo,
33 onLabelClick,
34}: BugRowProps) {
35 const isOpen = status === Status.Open;
36 const StatusIcon = isOpen ? CircleDot : CircleCheck;
37
38 return (
39 <div className="border-border hover:bg-muted/30 flex items-start gap-3 border-b px-4 py-3 last:border-0">
40 <StatusIcon
41 className={
42 isOpen
43 ? "mt-0.5 size-4 shrink-0 text-green-600 dark:text-green-400"
44 : "mt-0.5 size-4 shrink-0 text-purple-600 dark:text-purple-400"
45 }
46 />
47
48 <div className="min-w-0 flex-1">
49 <div className="flex flex-wrap items-baseline gap-2">
50 <Link
51 to="/$repo/issues/$id"
52 params={{ repo, id: humanId }}
53 className="text-foreground hover:text-primary font-medium hover:underline"
54 >
55 {title}
56 </Link>
57 {labels.map((label) => (
58 <LabelBadge
59 key={label.name}
60 name={label.name}
61 color={label.color}
62 onClick={onLabelClick}
63 />
64 ))}
65 </div>
66 <p className="text-muted-foreground mt-0.5 text-xs">
67 #{humanId} opened {formatDistanceToNow(new Date(createdAt), { addSuffix: true })} by{" "}
68 <Link
69 to="/$repo/user/$id"
70 params={{ repo, id: author.humanId }}
71 search={{ status: "open" as const, after: "" }}
72 className="hover:underline"
73 >
74 {author.displayName}
75 </Link>
76 </p>
77 </div>
78
79 {commentCount > 0 && (
80 <div className="text-muted-foreground flex shrink-0 items-center gap-1 text-xs">
81 <MessageSquare className="size-3.5" />
82 {commentCount}
83 </div>
84 )}
85 </div>
86 );
87}