import type * as React from "react";
import type {
EnvironmentId,
PullRequestCheck,
PullRequestChecksState,
PullRequestRef,
ScopedThreadRef,
} from "@t3tools/contracts";
import { useState } from "react";
import { useOpenLink } from "~/browser/useOpenLink";
import { cn } from "~/lib/utils";
import { pullRequestEnvironment } from "~/state/pullRequests";
import { useEnvironmentQuery } from "~/state/query";
import { Popover, PopoverPopup, PopoverTrigger } from "../ui/popover";
import { Button } from "../ui/button";
import { ScrollArea } from "../ui/scroll-area";
import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
import { toastManager } from "../ui/toast";
import { groupPullRequestChecks } from "./pullRequestDetail.logic";
import {
PullRequestCheckStatusIcon,
pullRequestCheckStatusLabel,
pullRequestChecksStatePresentation,
summarizePullRequestChecks,
} from "./pullRequestPresentation";
// Fetch only on open to avoid per-row requests.
function LazyChecksBody({
environmentId,
reference,
threadRef,
}: {
environmentId: EnvironmentId;
reference: PullRequestRef;
threadRef: ScopedThreadRef | null;
}) {
const detailQuery = useEnvironmentQuery(
pullRequestEnvironment.detail({ environmentId, input: reference }),
);
if (detailQuery.error !== null) {
return
{detailQuery.error}
;
}
if (detailQuery.data === null) {
return (
{detailQuery.isPending ? "Loading checks…" : "No checks reported"}
);
}
return ;
}
function ChecksBody({
checks,
threadRef,
}: {
checks: ReadonlyArray;
threadRef: ScopedThreadRef | null;
}) {
const openLink = useOpenLink(threadRef);
const [showAll, setShowAll] = useState(false);
const { attention, running, completed } = groupPullRequestChecks(checks);
const canCollapse = attention.length + running.length > 0 && completed.length > 0;
const visibleChecks = [...attention, ...running, ...(showAll || !canCollapse ? completed : [])];
const hasDetails = visibleChecks.some((check) => check.url !== null);
if (checks.length === 0) {
return No checks reported
;
}
return (
<>
{visibleChecks.map(
(check, index) => (
/* oxlint-disable react/no-array-index-key -- Check names can repeat; these status rows have no local state. */
{check.name}} />
{check.description ?? check.name}
{pullRequestCheckStatusLabel(check)}
{check.url === null ? null : (
{
if (!check.url) return;
void openLink(check.url).catch((error: unknown) => {
console.error(error);
toastManager.add({ type: "error", title: "Unable to open check details" });
});
}}
>
Details
)}
) /* oxlint-enable react/no-array-index-key */,
)}
{canCollapse ? (
setShowAll(!showAll)}
>
{showAll ? "Show less" : "Show all"}
) : null}
>
);
}
export function PullRequestChecksPopover({
checksState,
checks,
stale = false,
environmentId,
reference,
threadRef = null,
variant = "icon",
className,
render,
}: {
checksState: PullRequestChecksState;
checks?: ReadonlyArray;
stale?: boolean;
environmentId?: EnvironmentId;
reference?: PullRequestRef;
threadRef?: ScopedThreadRef | null;
variant?: "icon" | "count";
className?: string;
render?: React.ReactElement;
}) {
const presentation = pullRequestChecksStatePresentation(checksState);
const incompleteCount =
checks?.filter((check) => check.status === "pending" || check.status === "action-required")
.length ?? 0;
const failedCount =
checks?.filter((check) => check.status === "failure" || check.status === "cancelled").length ??
0;
const summary = checks === undefined || stale ? null : summarizePullRequestChecks(checks);
return (
)
) : (
)
}
onClick={(event) => event.stopPropagation()}
>
{variant === "count" ? (
<>
{checks !== undefined && incompleteCount > 0 ? (
{checks.length - incompleteCount}/{checks.length} checks
) : failedCount > 0 ? (
{failedCount} failed
) : null}
>
) : null}
{presentation.label}
{summary === null ? null : {summary}
}
{stale ? (
Check details are out of date. Refresh the pull request to update them.
) : checks !== undefined ? (
) : environmentId !== undefined && reference !== undefined ? (
) : null}
);
}