Files
kandev/apps/web/components/github/pr-task-status-summary.tsx
2026-09-10 23:39:56 +01:00

164 lines
5.5 KiB
TypeScript

"use client";
import type { TaskPR } from "@/lib/types/github";
import {
getMergeQueueSummaryDetail,
getMergeQueueSummaryStatus,
hasActiveMergeQueueEntry,
} from "./pr-merge-queue-status";
import {
ChangeRequestTaskStatusSummary,
type ChangeRequestTaskStatusSummaryData,
type ChangeRequestTaskSummaryRow,
type ChangeRequestTaskSummaryRowKind,
type ChangeRequestTaskSummaryStatus,
type ChangeRequestTaskSummaryTone,
} from "@/components/integrations/change-request-task-status-summary";
import {
getCurrentWorkflowAttention,
isWorkflowApprovalRequired,
workflowAttentionWorkflowNames,
} from "./pr-workflow-attention";
export type PRTaskSummaryRowKind = ChangeRequestTaskSummaryRowKind;
export type PRTaskSummaryTone = ChangeRequestTaskSummaryTone;
export type PRTaskSummaryStatus = ChangeRequestTaskSummaryStatus;
export type PRTaskSummaryRow = ChangeRequestTaskSummaryRow;
export type PRTaskStatusSummaryData = ChangeRequestTaskStatusSummaryData;
function rawRow(kind: PRTaskSummaryRowKind, rawValue: string): PRTaskSummaryRow {
return { kind, status: "raw", tone: "muted", rawValue };
}
function deriveStateRow(state: string): PRTaskSummaryRow | null {
if (!state || state === "open") return null;
if (state === "merged") return { kind: "state", status: "merged", tone: "merged" };
if (state === "closed") return { kind: "state", status: "closed", tone: "danger" };
return rawRow("state", state);
}
function deriveReviewRow(reviewState: string): PRTaskSummaryRow | null {
if (!reviewState) return null;
if (reviewState === "approved") {
return { kind: "review", status: "approved", tone: "success" };
}
if (reviewState === "changes_requested") {
return { kind: "review", status: "changes_requested", tone: "danger" };
}
if (reviewState === "pending") {
return { kind: "review", status: "pending_review", tone: "info" };
}
return rawRow("review", reviewState);
}
function workflowAttentionRow(pr: TaskPR): PRTaskSummaryRow | null {
const attention = getCurrentWorkflowAttention(pr);
if (!attention) return null;
if (attention.state === "unknown") {
return {
kind: "ci",
id: "workflow-attention",
status: "workflow_unavailable",
tone: "muted",
};
}
if (attention.state !== "approval_required" && attention.state !== "action_required") {
return null;
}
const names = workflowAttentionWorkflowNames(attention);
return {
kind: "ci",
id: "workflow-attention",
status: isWorkflowApprovalRequired(attention) ? "awaiting_approval" : "workflow_attention",
tone: "warning",
...(names
? {
detail: {
key: "github:workflowAttentionWorkflows",
values: { names },
},
}
: {}),
};
}
function deriveCIRows(pr: TaskPR): PRTaskSummaryRow[] {
const attentionRow = workflowAttentionRow(pr);
const attentionExplainsUnstable =
attentionRow?.status === "awaiting_approval" || attentionRow?.status === "workflow_attention";
const rows: PRTaskSummaryRow[] = [];
if (pr.checks_state === "success") rows.push({ kind: "ci", status: "passed", tone: "success" });
else if (pr.checks_state === "failure") {
rows.push({ kind: "ci", status: "failed", tone: "danger" });
} else if (pr.checks_state === "pending") {
rows.push({ kind: "ci", status: "in_progress", tone: "warning" });
} else if (
pr.checks_state === "unstable" ||
(pr.mergeable_state === "unstable" && !attentionExplainsUnstable)
) {
if (!attentionExplainsUnstable) {
rows.push({ kind: "ci", status: "checks_not_successful", tone: "warning" });
}
} else if (pr.checks_state) rows.push(rawRow("ci", pr.checks_state));
if (attentionRow) rows.push(attentionRow);
return rows;
}
function deriveMergeRow(pr: TaskPR, readyToMerge: boolean): PRTaskSummaryRow | null {
if (pr.state !== "open") return null;
if (hasActiveMergeQueueEntry(pr)) {
return {
kind: "merge",
status: getMergeQueueSummaryStatus(pr.merge_queue_state),
tone: "queued",
detail: getMergeQueueSummaryDetail(
pr.merge_queue_position,
pr.merge_queue_estimated_time_to_merge_seconds,
),
};
}
if (pr.mergeable_state === "draft") {
return { kind: "merge", status: "draft", tone: "muted" };
}
if (readyToMerge) return { kind: "merge", status: "ready", tone: "success" };
if (!pr.mergeable_state || pr.mergeable_state === "unknown") return null;
if (pr.mergeable_state === "dirty") {
return { kind: "merge", status: "conflicts", tone: "danger" };
}
if (pr.mergeable_state === "behind") {
return { kind: "merge", status: "behind", tone: "warning" };
}
if (pr.mergeable_state === "blocked") {
return { kind: "merge", status: "blocked", tone: "muted" };
}
if (pr.mergeable_state === "clean") {
return { kind: "merge", status: "mergeable", tone: "muted" };
}
if (pr.mergeable_state === "unstable") return null;
return rawRow("merge", pr.mergeable_state);
}
export function derivePRTaskStatusSummary(
pr: TaskPR,
readyToMerge: boolean,
): PRTaskStatusSummaryData {
const rows = [
deriveStateRow(pr.state),
deriveReviewRow(pr.review_state),
...deriveCIRows(pr),
deriveMergeRow(pr, readyToMerge),
].filter((row): row is PRTaskSummaryRow => row !== null);
const author = pr.author_login.trim();
return {
number: pr.pr_number,
title: pr.pr_title,
...(author ? { author } : {}),
rows,
};
}
export function PRTaskStatusSummary({ summaries }: { summaries: PRTaskStatusSummaryData[] }) {
return <ChangeRequestTaskStatusSummary summaries={summaries} />;
}