mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-27 04:56:20 +02:00
This reverts commit 139cc89200.
Co-authored-by: Bohan-J <bohan@devv.ai>
Co-authored-by: multica-agent <github@multica.ai>
102 lines
4.2 KiB
TypeScript
102 lines
4.2 KiB
TypeScript
import type { GitHubPullRequest } from "../types";
|
||
|
||
// Status kinds rendered in the PR sidebar row's detail line. Order in the
|
||
// pass-through table matters — the first matching rule wins. The order is
|
||
// chosen so terminal PR states (closed / merged) short-circuit before any
|
||
// transient CI/conflict signal, since those signals are no longer actionable
|
||
// on a terminal PR.
|
||
//
|
||
// Priority (high → low):
|
||
// 1. closed (not merged) → status_closed
|
||
// 2. merged → status_merged
|
||
// 3. mergeable_state = "dirty" → status_conflicts
|
||
// 4. any failed suite → status_checks_failed
|
||
// 5. any pending suite → status_checks_pending
|
||
// 6. any passed suite → status_checks_passed
|
||
// 7. no suite + mergeable=clean → status_ready
|
||
// 8. otherwise → status_unknown
|
||
//
|
||
// Note: this table is the single source of truth for the sidebar PR row. The
|
||
// older row-with-badges implementation used a separate "hide status row for
|
||
// terminal PRs" branch — the current row renders
|
||
// with status_closed / status_merged text, never falling through to a
|
||
// conflicts / checks line on a terminal PR. Keep this priority order in sync
|
||
// with the i18n keys `pull_request_card_status_*` and with the progress-strip
|
||
// derivation in `derivePullRequestProgressSegments` (terminal kinds get a
|
||
// solid bar; the rest map onto the per-suite counts).
|
||
export type PullRequestStatusKind =
|
||
| "closed"
|
||
| "merged"
|
||
| "conflicts"
|
||
| "checks_failed"
|
||
| "checks_pending"
|
||
| "checks_passed"
|
||
| "ready"
|
||
| "unknown";
|
||
|
||
export interface PullRequestStatusInput {
|
||
state: GitHubPullRequest["state"];
|
||
mergeable_state?: string | null;
|
||
checks_failed?: number;
|
||
checks_pending?: number;
|
||
checks_passed?: number;
|
||
}
|
||
|
||
export function derivePullRequestStatusKind(input: PullRequestStatusInput): PullRequestStatusKind {
|
||
if (input.state === "closed") return "closed";
|
||
if (input.state === "merged") return "merged";
|
||
if (input.mergeable_state === "dirty") return "conflicts";
|
||
if ((input.checks_failed ?? 0) > 0) return "checks_failed";
|
||
if ((input.checks_pending ?? 0) > 0) return "checks_pending";
|
||
if ((input.checks_passed ?? 0) > 0) return "checks_passed";
|
||
if (input.mergeable_state === "clean") return "ready";
|
||
return "unknown";
|
||
}
|
||
|
||
export interface PullRequestProgressSegment {
|
||
kind: "failed" | "pending" | "passed";
|
||
ratio: number;
|
||
}
|
||
|
||
// Segmented progress bar input. Returns null when:
|
||
// - the PR is terminal (closed/merged) — the card paints a solid bar
|
||
// in a state-specific color, no segmentation needed;
|
||
// - no check_suite has been observed (total === 0) — the card hides
|
||
// the bar entirely.
|
||
// Otherwise emits the segments left-to-right: failed → pending → passed.
|
||
// "Failure first" is intentional: problems should be visible before signal
|
||
// that everything is fine.
|
||
export function derivePullRequestProgressSegments(
|
||
input: PullRequestStatusInput,
|
||
): PullRequestProgressSegment[] | null {
|
||
if (input.state === "closed" || input.state === "merged") return null;
|
||
const failed = input.checks_failed ?? 0;
|
||
const pending = input.checks_pending ?? 0;
|
||
const passed = input.checks_passed ?? 0;
|
||
const total = failed + pending + passed;
|
||
if (total === 0) return null;
|
||
const segments: PullRequestProgressSegment[] = [];
|
||
if (failed > 0) segments.push({ kind: "failed", ratio: failed / total });
|
||
if (pending > 0) segments.push({ kind: "pending", ratio: pending / total });
|
||
if (passed > 0) segments.push({ kind: "passed", ratio: passed / total });
|
||
return segments;
|
||
}
|
||
|
||
export interface PullRequestStatsInput {
|
||
additions?: number;
|
||
deletions?: number;
|
||
changed_files?: number;
|
||
}
|
||
|
||
// shouldShowPullRequestStats encodes the "old backend → new frontend" guard:
|
||
// when the backend that served this PR row doesn't know about the stats
|
||
// columns yet, every numeric field defaults to 0. Rendering "+0 −0 · 0 files"
|
||
// in that case would be a lie (the PR almost certainly has real changes),
|
||
// so we hide the entire stats row until at least one signal is non-zero.
|
||
export function shouldShowPullRequestStats(input: PullRequestStatsInput): boolean {
|
||
const a = input.additions ?? 0;
|
||
const d = input.deletions ?? 0;
|
||
const f = input.changed_files ?? 0;
|
||
return a + d + f > 0;
|
||
}
|