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 /** Current repo slug, used to build /:repo/issues/:id and /:repo/user/:id links. */
19 repo: string | null;
20 onLabelClick?: (name: string) => void;
21}
22
23// Single row in the issue list. Shows status icon, title, labels, author and
24// comment count. Labels are clickable to filter the list by that label.
25export function BugRow({
26 humanId,
27 status,
28 title,
29 labels,
30 author,
31 createdAt,
32 commentCount,
33 repo,
34 onLabelClick,
35}: BugRowProps) {
36 const isOpen = status === Status.Open;
37 const StatusIcon = isOpen ? CircleDot : CircleCheck;
38
39 const issueHref = repo ? `/${repo}/issues/${humanId}` : `/issues/${humanId}`;
40 const authorHref = repo ? `/${repo}/user/${author.humanId}` : `/user/${author.humanId}`;
41
42 return (
43 <div className="border-border hover:bg-muted/30 flex items-start gap-3 border-b px-4 py-3 last:border-0">
44 <StatusIcon
45 className={
46 isOpen
47 ? "mt-0.5 size-4 shrink-0 text-green-600 dark:text-green-400"
48 : "mt-0.5 size-4 shrink-0 text-purple-600 dark:text-purple-400"
49 }
50 />
51
52 <div className="min-w-0 flex-1">
53 <div className="flex flex-wrap items-baseline gap-2">
54 <Link
55 to={issueHref}
56 className="text-foreground hover:text-primary font-medium hover:underline"
57 >
58 {title}
59 </Link>
60 {labels.map((label) => (
61 <LabelBadge
62 key={label.name}
63 name={label.name}
64 color={label.color}
65 onClick={onLabelClick}
66 />
67 ))}
68 </div>
69 <p className="text-muted-foreground mt-0.5 text-xs">
70 #{humanId} opened {formatDistanceToNow(new Date(createdAt), { addSuffix: true })} by{" "}
71 <Link to={authorHref} className="hover:underline">
72 {author.displayName}
73 </Link>
74 </p>
75 </div>
76
77 {commentCount > 0 && (
78 <div className="text-muted-foreground flex shrink-0 items-center gap-1 text-xs">
79 <MessageSquare className="size-3.5" />
80 {commentCount}
81 </div>
82 )}
83 </div>
84 );
85}