BugRow.tsx

 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            className="hover:underline"
72          >
73            {author.displayName}
74          </Link>
75        </p>
76      </div>
77
78      {commentCount > 0 && (
79        <div className="text-muted-foreground flex shrink-0 items-center gap-1 text-xs">
80          <MessageSquare className="size-3.5" />
81          {commentCount}
82        </div>
83      )}
84    </div>
85  );
86}