"use client"; import Link from "next/link"; import { IconGitPullRequest, IconGitPullRequestClosed, IconGitMerge, IconPlus, IconChevronDown, } from "@tabler/icons-react"; import type { Icon } from "@tabler/icons-react"; import { Button } from "@kandev/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@kandev/ui/dropdown-menu"; import { Spinner } from "@kandev/ui/spinner"; import { cn, formatRelativeTime } from "@/lib/utils"; import type { GitHubPR, GitHubPRStatus, TaskPR } from "@/lib/types/github"; import type { LaunchPayload, TaskPreset } from "./quick-task-launcher"; import { PRStatusBadges } from "./pr-status-badges"; import { prStatusKey, usePRStatuses } from "./use-pr-statuses"; import { PRRowTaskIndicator } from "./pr-row-task-indicator"; type PRListProps = { items: GitHubPR[]; loading: boolean; error: string | null; presets: TaskPreset[]; onStartTask: (payload: LaunchPayload) => void; prKeyToTasks?: Map; }; // Prefer the enriched PR returned by the batched status endpoint — the search // API used to populate `items` does not include head/base branches, so the // launcher needs the enriched copy to pre-fill the task dialog correctly. export function pickPRForLaunch(pr: GitHubPR, status: GitHubPRStatus | null | undefined): GitHubPR { return status?.pr ?? pr; } function prStateIcon(pr: GitHubPR): { Icon: Icon; className: string } { if (pr.state === "merged") return { Icon: IconGitMerge, className: "text-purple-600 dark:text-purple-400" }; if (pr.state === "closed") return { Icon: IconGitPullRequestClosed, className: "text-red-600 dark:text-red-400" }; if (pr.draft) return { Icon: IconGitPullRequest, className: "text-muted-foreground" }; return { Icon: IconGitPullRequest, className: "text-emerald-600 dark:text-emerald-400" }; } function StartTaskMenu({ pr, presets, onStartTask, }: { pr: GitHubPR; presets: TaskPreset[]; onStartTask: PRListProps["onStartTask"]; }) { const launch = (preset: TaskPreset) => onStartTask({ kind: "pr", pr, preset }); return ( {presets.map((p) => { const ItemIcon = p.icon; return ( launch(p)} data-testid="pr-start-task-preset" data-preset-id={p.id} >
{p.label} {p.hint}
); })}
); } function PRRow({ pr, status, presets, onStartTask, tasks, }: { pr: GitHubPR; status: GitHubPRStatus | null | undefined; presets: TaskPreset[]; onStartTask: PRListProps["onStartTask"]; tasks: TaskPR[] | undefined; }) { const { Icon: StateIcon, className: stateIconClass } = prStateIcon(pr); return (
{pr.title}
{pr.repo_owner}/{pr.repo_name}#{pr.number} · by {pr.author_login} · opened {formatRelativeTime(pr.created_at)}
); } function PRListBody({ loading, error, items, presets, onStartTask, prKeyToTasks }: PRListProps) { const statuses = usePRStatuses(items); if (loading) { return (
); } if (error) { return
{error}
; } if (items.length === 0) { return (
No pull requests match this filter.
); } return (
{items.map((pr) => { const key = prStatusKey(pr.repo_owner, pr.repo_name, pr.number); return ( ); })}
); } export function PRList(props: PRListProps) { return (
); }