diff --git a/apps/dashboard/src/components/pulls/detail/pull-detail-activity.tsx b/apps/dashboard/src/components/pulls/detail/pull-detail-activity.tsx
index 2513974..cf05c01 100644
--- a/apps/dashboard/src/components/pulls/detail/pull-detail-activity.tsx
+++ b/apps/dashboard/src/components/pulls/detail/pull-detail-activity.tsx
@@ -62,6 +62,7 @@ import {
groupTimelineEvents,
} from "#/components/details/grouped-label-event";
import { LabelPill } from "#/components/details/label-pill";
+import { useMergeBypass } from "#/components/pulls/detail/use-merge-bypass";
import { formatRelativeTime } from "#/lib/format-relative-time";
import {
deleteBranch,
@@ -424,6 +425,7 @@ function MergeStatusCard({
baseRefName,
canUpdateBranch,
canBypassProtections,
+ canMerge,
} = status;
const approvedReviews = reviews.filter((r) => r.state === "APPROVED");
@@ -439,6 +441,17 @@ function MergeStatusCard({
const hasConflicts = mergeableState === "dirty";
const isMergeBlocked = mergeableState === "blocked" || mergeable === false;
+ const bypass = useMergeBypass({
+ isMergeBlocked,
+ canBypassProtections,
+ hasCheckFailures,
+ hasReviewIssue,
+ hasConflicts,
+ isBehind,
+ allChecksPassed,
+ totalChecks: checks.total,
+ });
+
return (
{/* Reviews section */}
@@ -490,7 +503,8 @@ function MergeStatusCard({
{/* Merge action footer */}
;
owner: string;
repo: string;
pullNumber: number;
@@ -1005,7 +1021,6 @@ function MergeFooter({
"squash",
);
const [isMerging, setIsMerging] = useState(false);
- const [bypassChecks, setBypassChecks] = useState(false);
const queryClient = useQueryClient();
const currentStrategy =
@@ -1021,7 +1036,7 @@ function MergeFooter({
repo,
pullNumber,
mergeMethod,
- bypassProtections: bypassChecks,
+ bypassProtections: bypass.shouldBypass,
},
});
if (result.ok) {
@@ -1037,7 +1052,8 @@ function MergeFooter({
}
};
- const isDisabled = (isMergeBlocked && !bypassChecks) || isMerging;
+ const isDisabled =
+ !canMerge || (isMergeBlocked && !bypass.shouldBypass) || isMerging;
return (
@@ -1093,18 +1109,22 @@ function MergeFooter({
- {isMergeBlocked && !bypassChecks && (
+ {!canMerge ? (
+
+ You don't have permission to merge this pull request.
+
+ ) : isMergeBlocked && !bypass.shouldBypass ? (
Merging is blocked — all required conditions have not been met.
- )}
+ ) : null}
- {isMergeBlocked && canBypassProtections && (
+ {bypass.showOption && (
setBypassChecks(checked === true)}
+ checked={bypass.checked}
+ onCheckedChange={(checked) => bypass.setChecked(checked === true)}
/>