The report had an approvals column (1/2) and a separate approved column. They are one fact, so approvals now carries the filter, complete or missing, and the approved column is gone.
83 lines
3 KiB
TypeScript
83 lines
3 KiB
TypeScript
// Pure pieces of the PR report: the row the controller returns, the report
|
||
// URL for a repo filter, and the words its flag columns filter on. Kept out of
|
||
// the page so the cases are unit tested.
|
||
|
||
/** One open pull request, as `GET /api/pr-report` returns it. */
|
||
export interface PrReportRow {
|
||
/** `owner/name`. */
|
||
repo: string;
|
||
number: number;
|
||
title: string;
|
||
html_url: string | null;
|
||
author: string | null;
|
||
labels: string[];
|
||
assignees: string[];
|
||
requested_reviewers: string[];
|
||
base_branch: string;
|
||
head_branch: string;
|
||
created_unix: number | null;
|
||
updated_unix: number | null;
|
||
/** Lines the PR adds and removes. */
|
||
additions: number | null;
|
||
deletions: number | null;
|
||
/** The forge's draft flag. */
|
||
wip: boolean;
|
||
has_conflicts: boolean;
|
||
/** The base branch has a branch protection rule. */
|
||
targets_protected_branch: boolean;
|
||
required_approvals: number;
|
||
approvals: number;
|
||
/** Reviewers whose latest review asks for changes. */
|
||
changes_requested: number;
|
||
/** `null` when the head has no commit status. */
|
||
ci: "success" | "pending" | "failure" | null;
|
||
}
|
||
|
||
/** The report URL for a repo filter. The path parts are encoded, so a name
|
||
* with `?`, `#` or `%` stays one path segment. A filter without a slash is
|
||
* not a repo, so it reads as all repos. */
|
||
export function prReportUrl(repoFilter: string): string {
|
||
const i = repoFilter.indexOf("/");
|
||
if (i < 0) return "/api/pr-report";
|
||
const org = encodeURIComponent(repoFilter.slice(0, i));
|
||
const name = encodeURIComponent(repoFilter.slice(i + 1));
|
||
return `/api/repos/${org}/${name}/pr-report`;
|
||
}
|
||
|
||
/** What a flag column offers as filter values. */
|
||
export function yesNo(flag: boolean): string {
|
||
return flag ? "yes" : "no";
|
||
}
|
||
|
||
/** The approvals meet the base branch's rule. An unprotected base asks for
|
||
* none, so its PRs have them all. */
|
||
export function hasAllApprovals(row: PrReportRow): boolean {
|
||
return row.approvals >= row.required_approvals;
|
||
}
|
||
|
||
/** What the approvals column filters on: whether the PR has all the approvals
|
||
* its base branch asks for. */
|
||
export function approvalsState(row: PrReportRow): "complete" | "missing" {
|
||
return hasAllApprovals(row) ? "complete" : "missing";
|
||
}
|
||
|
||
/** The diff size cell: lines added and removed, `+120 −8`. A count the forge
|
||
* did not give reads as a dash. */
|
||
export function diffText(row: PrReportRow): string {
|
||
if (row.additions === null && row.deletions === null) return "—";
|
||
return `+${row.additions ?? 0} −${row.deletions ?? 0}`;
|
||
}
|
||
|
||
/** What the diff column sorts on: lines changed in all. */
|
||
export function diffSize(row: PrReportRow): number {
|
||
return (row.additions ?? 0) + (row.deletions ?? 0);
|
||
}
|
||
|
||
/** The approvals cell: how many of the rule's approvals the PR has, or a dash
|
||
* when its base branch asks for none. */
|
||
export function approvalsText(row: PrReportRow): string {
|
||
if (!row.targets_protected_branch || row.required_approvals === 0) {
|
||
return row.approvals > 0 ? `${row.approvals}` : "—";
|
||
}
|
||
return `${row.approvals}/${row.required_approvals}`;
|
||
}
|