Detailed changes
@@ -10,8 +10,8 @@ import {
BugDetailDocument,
} from "@/__generated__/graphql";
import { Markdown } from "@/components/content/Markdown";
-import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
+import * as CommentCard from "@/components/ui/comment-card";
import { Textarea } from "@/components/ui/textarea";
import * as WritePreview from "@/components/ui/write-preview";
import { useAuth } from "@/lib/auth";
@@ -73,15 +73,9 @@ export function CommentBox({ bugPrefix, bugStatus, ref_ }: CommentBoxProps) {
if (!user) return null;
return (
- <div className="flex gap-3">
- <Avatar className="mt-1 size-8 shrink-0">
- <AvatarImage src={user.avatarUrl ?? undefined} alt={user.displayName} />
- <AvatarFallback className="text-xs">
- {user.displayName.slice(0, 2).toUpperCase()}
- </AvatarFallback>
- </Avatar>
-
- <div className="border-border min-w-0 flex-1 rounded-md border">
+ <CommentCard.Root>
+ <CommentCard.AuthorAvatar src={user.avatarUrl} name={user.displayName} />
+ <CommentCard.Card>
<WritePreview.Root hasContent={hasMessage} preview={preview} onPreviewChange={setPreview}>
<WritePreview.Tabs className="border-border border-b px-4 py-2" />
<WritePreview.WriteSlot>
@@ -122,7 +116,7 @@ export function CommentBox({ bugPrefix, bugStatus, ref_ }: CommentBoxProps) {
Comment
</Button>
</div>
- </div>
- </div>
+ </CommentCard.Card>
+ </CommentCard.Root>
);
}
@@ -2,6 +2,7 @@ import { Settings2 } from "lucide-react";
import { useBugChangeLabelsMutation, BugDetailDocument } from "@/__generated__/graphql";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
+import { SectionHeading } from "@/components/ui/section-heading";
import { useAuth } from "@/lib/auth";
import { LabelBadge } from "./LabelBadge";
@@ -42,9 +43,7 @@ export function LabelEditor({ bugPrefix, currentLabels, ref_, validLabels }: Lab
return (
<div>
<div className="mb-2 flex items-center justify-between">
- <h3 className="text-muted-foreground text-xs font-semibold tracking-wider uppercase">
- Labels
- </h3>
+ <SectionHeading className="mb-0">Labels</SectionHeading>
{user && validLabels.length > 0 && (
<Popover>
<PopoverTrigger asChild>
@@ -10,8 +10,8 @@ import {
BugDetailDocument,
} from "@/__generated__/graphql";
import { Markdown } from "@/components/content/Markdown";
-import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
+import * as CommentCard from "@/components/ui/comment-card";
import { Textarea } from "@/components/ui/textarea";
import { useAuth } from "@/lib/auth";
@@ -94,16 +94,10 @@ function CommentItem({
const canEdit = user !== null && user.id === item.author.id;
return (
- <div className="flex gap-3">
- <Avatar className="mt-1 size-8 shrink-0">
- <AvatarImage src={item.author.avatarUrl ?? undefined} alt={item.author.displayName} />
- <AvatarFallback className="text-xs">
- {item.author.displayName.slice(0, 2).toUpperCase()}
- </AvatarFallback>
- </Avatar>
-
- <div className="border-border min-w-0 flex-1 rounded-md border">
- <div className="border-border bg-muted/40 flex items-center gap-2 border-b px-4 py-2 text-sm">
+ <CommentCard.Root>
+ <CommentCard.AuthorAvatar src={item.author.avatarUrl} name={item.author.displayName} />
+ <CommentCard.Card>
+ <CommentCard.CardHeader>
<Link
to="/$repo/user/$id"
params={{ repo: repo!, id: item.author.humanId }}
@@ -124,11 +118,10 @@ function CommentItem({
Edit
</button>
)}
- </div>
+ </CommentCard.CardHeader>
{editing ? (
<div className="space-y-2 p-3">
- {/* Ctrl/Cmd+Enter saves; Escape cancels — standard editor shortcuts */}
<Textarea
value={editValue}
onChange={(e) => setEditValue(e.target.value)}
@@ -152,16 +145,16 @@ function CommentItem({
</div>
</div>
) : (
- <div className="px-4 py-3">
+ <CommentCard.CardBody>
{item.message ? (
<Markdown content={item.message} />
) : (
<p className="text-muted-foreground text-sm italic">No description provided.</p>
)}
- </div>
+ </CommentCard.CardBody>
)}
- </div>
- </div>
+ </CommentCard.Card>
+ </CommentCard.Root>
);
}
@@ -261,83 +261,83 @@ exports[`FileViewer/TypeScriptFile matches snapshot 1`] = `
>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 38.89277215488839%;"
+ style="width: 71.50143759316606%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 30.59424948464735%;"
+ style="width: 60.6708169292491%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 88.8718618920598%;"
+ style="width: 73.2183590812158%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 76.71451982934077%;"
+ style="width: 34.976257221082484%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 83.74909292446992%;"
+ style="width: 61.48920656229181%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 30.204986115614368%;"
+ style="width: 49.69450792480475%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 43.630255749606825%;"
+ style="width: 54.37099249060006%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 65.07221950291687%;"
+ style="width: 76.59901111896043%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 52.442063845559616%;"
+ style="width: 44.36460693491388%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 65.54311299024468%;"
+ style="width: 42.028325445555126%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 82.36906592981144%;"
+ style="width: 37.66834389435642%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 83.59354501082503%;"
+ style="width: 57.91664167237783%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 48.94220651251563%;"
+ style="width: 46.78288981850762%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 59.74254935831376%;"
+ style="width: 63.98546670165045%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 58.093030580736645%;"
+ style="width: 75.9276300696755%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 40.43812857099223%;"
+ style="width: 47.38236735204427%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 78.25416747608861%;"
+ style="width: 77.0934909916055%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 57.060810833798755%;"
+ style="width: 48.844870253597435%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 56.43045048200637%;"
+ style="width: 43.285422031290835%;"
/>
<div
class="animate-pulse rounded-md bg-primary/10 h-3.5"
- style="width: 73.65573925757732%;"
+ style="width: 55.99529760322575%;"
/>
</div>
</div>
@@ -10,6 +10,8 @@ import { Timeline } from "@/components/bugs/Timeline";
import { TitleEditor } from "@/components/bugs/TitleEditor";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { BackLink } from "@/components/ui/back-link";
+import { EmptyState } from "@/components/ui/empty-state";
+import { SectionHeading } from "@/components/ui/section-heading";
import { Separator } from "@/components/ui/separator";
import { Skeleton } from "@/components/ui/skeleton";
@@ -37,7 +39,7 @@ function RouteComponent() {
const bug = data?.repository?.bug;
if (!bug) {
- return <div className="text-muted-foreground py-16 text-center text-sm">Issue not found.</div>;
+ return <EmptyState className="py-16">Issue not found.</EmptyState>;
}
return (
@@ -87,9 +89,7 @@ function RouteComponent() {
<Separator />
<div>
- <h3 className="text-muted-foreground mb-2 text-xs font-semibold tracking-wider uppercase">
- Participants
- </h3>
+ <SectionHeading>Participants</SectionHeading>
<div className="flex flex-wrap gap-1.5">
{bug.participants.nodes.map((p) => {
return (
@@ -1,7 +1,7 @@
import { useReadQuery } from "@apollo/client/react";
import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
import { formatDistanceToNow } from "date-fns";
-import { CircleDot, CircleCheck, ChevronLeft, ChevronRight, Search } from "lucide-react";
+import { CircleDot, CircleCheck, Search } from "lucide-react";
import { useMemo, useState } from "react";
import * as v from "valibot";
@@ -11,7 +11,8 @@ import type { SortValue } from "@/components/bugs/IssueFilters";
import * as IssueRow from "@/components/bugs/IssueRow";
import { LabelBadgeLink } from "@/components/bugs/LabelBadge";
import { Button } from "@/components/ui/button";
-import { ButtonLink } from "@/components/ui/button-link";
+import { EmptyState } from "@/components/ui/empty-state";
+import * as Pagination from "@/components/ui/pagination";
import * as QueryInput from "@/components/ui/query-input";
import type { CompletionProvider } from "@/components/ui/query-input";
import { Skeleton } from "@/components/ui/skeleton";
@@ -253,9 +254,7 @@ function RouteComponent() {
{/* Bug rows */}
{bugs?.nodes.length === 0 && (
- <p className="text-muted-foreground px-4 py-8 text-center text-sm">
- No {statusFilter} issues found.
- </p>
+ <EmptyState>No {statusFilter} issues found.</EmptyState>
)}
{bugs?.nodes.map((bug) => (
@@ -311,35 +310,21 @@ function RouteComponent() {
))}
{totalPages > 1 && (
- <div className="border-border flex items-center justify-center gap-2 border-t px-4 py-2">
- <ButtonLink
+ <Pagination.Root>
+ <Pagination.Previous
to="/$repo/issues"
- params={{ repo: repo }}
+ params={{ repo }}
search={{ q, after: "" }}
- variant="ghost"
- size="sm"
disabled={!hasPrev}
- className="text-muted-foreground gap-1"
- >
- <ChevronLeft className="size-4" />
- Previous
- </ButtonLink>
- <span className="text-muted-foreground text-sm">
- Page {after ? 2 : 1} of {totalPages}
- </span>
- <ButtonLink
+ />
+ <Pagination.Info>Page {after ? 2 : 1} of {totalPages}</Pagination.Info>
+ <Pagination.Next
to="/$repo/issues"
- params={{ repo: repo }}
+ params={{ repo }}
search={{ q, after: bugs?.pageInfo.endCursor ?? "" }}
- variant="ghost"
- size="sm"
disabled={!hasNext}
- className="text-muted-foreground gap-1"
- >
- Next
- <ChevronRight className="size-4" />
- </ButtonLink>
- </div>
+ />
+ </Pagination.Root>
)}
</div>
</div>
@@ -10,8 +10,6 @@ import {
CircleDot,
CircleCheck,
ShieldCheck,
- ChevronLeft,
- ChevronRight,
} from "lucide-react";
import * as v from "valibot";
@@ -20,7 +18,8 @@ import * as IssueRow from "@/components/bugs/IssueRow";
import { LabelBadge } from "@/components/bugs/LabelBadge";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { BackLink } from "@/components/ui/back-link";
-import { ButtonLink } from "@/components/ui/button-link";
+import { EmptyState } from "@/components/ui/empty-state";
+import * as Pagination from "@/components/ui/pagination";
import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
@@ -165,9 +164,7 @@ function RouteComponent() {
</div>
{bugs?.nodes.length === 0 && (
- <p className="text-muted-foreground px-4 py-8 text-center text-sm">
- No {statusFilter} issues.
- </p>
+ <EmptyState>No {statusFilter} issues.</EmptyState>
)}
{bugs?.nodes.map((bug) => (
@@ -196,35 +193,21 @@ function RouteComponent() {
))}
{totalPages > 1 && (
- <div className="border-border flex items-center justify-center gap-2 border-t px-4 py-2">
- <ButtonLink
+ <Pagination.Root>
+ <Pagination.Previous
to="/$repo/user/$id"
params={{ repo, id }}
search={{ status: statusFilter, after: "" }}
- variant="ghost"
- size="sm"
disabled={!hasPrev}
- className="text-muted-foreground gap-1"
- >
- <ChevronLeft className="size-4" />
- Previous
- </ButtonLink>
- <span className="text-muted-foreground text-sm">
- Page {after ? 2 : 1} of {totalPages}
- </span>
- <ButtonLink
+ />
+ <Pagination.Info>Page {after ? 2 : 1} of {totalPages}</Pagination.Info>
+ <Pagination.Next
to="/$repo/user/$id"
params={{ repo, id }}
search={{ status: statusFilter, after: bugs?.pageInfo.endCursor ?? "" }}
- variant="ghost"
- size="sm"
disabled={!hasNext}
- className="text-muted-foreground gap-1"
- >
- Next
- <ChevronRight className="size-4" />
- </ButtonLink>
- </div>
+ />
+ </Pagination.Root>
)}
</div>
</div>