// User profile page (/user/:id). Fetches an identity by prefix and shows: // - avatar, display name, login, email, humanId, protected badge // - open/closed issue toggle with BOTH counts always visible // - paginated list of that user's bugs (cursor-stack, same approach as BugListPage) // // The :id param is treated as a humanId prefix and passed directly to the // identity(prefix) and allBugs(query:"author:...") GraphQL arguments. import { useState } from 'react' import { useParams, Link } from 'react-router-dom' import { formatDistanceToNow } from 'date-fns' import { ArrowLeft, MessageSquare, CircleDot, CircleCheck, ShieldCheck, ChevronLeft, ChevronRight, } from 'lucide-react' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' import { Button } from '@/components/ui/button' import { Skeleton } from '@/components/ui/skeleton' import { LabelBadge } from '@/components/bugs/LabelBadge' import { cn } from '@/lib/utils' import { Status, useUserProfileQuery } from '@/__generated__/graphql' import { useRepo } from '@/lib/repo' const PAGE_SIZE = 25 export function UserProfilePage() { const { id } = useParams<{ id: string }>() const repo = useRepo() const [statusFilter, setStatusFilter] = useState<'open' | 'closed'>('open') // Cursor-stack pagination: cursors[i] is the `after` value to fetch page i. // Resetting to [undefined] returns to page 1. Shared pattern with BugListPage. const [cursors, setCursors] = useState<(string | undefined)[]>([undefined]) const page = cursors.length - 1 // Three allBugs aliases in one round-trip: // openCount / closedCount — always fetched so both badge numbers are visible // bugs — paginated list for the selected tab const { data, loading, error } = useUserProfileQuery({ variables: { ref: repo, prefix: id!, openQuery: `author:${id} status:open`, closedQuery: `author:${id} status:closed`, listQuery: `author:${id} status:${statusFilter}`, after: cursors[page], }, }) function switchStatus(next: 'open' | 'closed') { if (next === statusFilter) return setStatusFilter(next) setCursors([undefined]) // reset to page 1 on tab change } if (error) { return (
@{identity.login}
} {identity.email &&{identity.email}
}#{identity.humanId}
No {statusFilter} issues.
)} {bugs?.nodes.map((bug) => { const isOpen = bug.status === Status.Open const StatusIcon = isOpen ? CircleDot : CircleCheck return (#{bug.humanId} opened{' '} {formatDistanceToNow(new Date(bug.createdAt), { addSuffix: true })}