From 8e9cda15733c013cd23e0de281a8eb15fd926d52 Mon Sep 17 00:00:00 2001 From: Maxwell <145994855+ping-maxwell@users.noreply.github.com> Date: Tue, 10 Mar 2026 18:59:25 -0700 Subject: [PATCH] feat: merge sheet (#315) --- .../[owner]/[repo]/pulls/[number]/page.tsx | 7 + apps/web/src/components/pr/pr-merge-panel.tsx | 524 +++++++++--------- 2 files changed, 259 insertions(+), 272 deletions(-) diff --git a/apps/web/src/app/(app)/repos/[owner]/[repo]/pulls/[number]/page.tsx b/apps/web/src/app/(app)/repos/[owner]/[repo]/pulls/[number]/page.tsx index 7323a20..c562175 100644 --- a/apps/web/src/app/(app)/repos/[owner]/[repo]/pulls/[number]/page.tsx +++ b/apps/web/src/app/(app)/repos/[owner]/[repo]/pulls/[number]/page.tsx @@ -602,6 +602,13 @@ export default async function PRDetailPage({ branchBehindBase ?? false } + checkStatus={ + checkStatus && + checkStatus.total > + 0 + ? checkStatus + : undefined + } /> } diff --git a/apps/web/src/components/pr/pr-merge-panel.tsx b/apps/web/src/components/pr/pr-merge-panel.tsx index 22f2100..373c888 100644 --- a/apps/web/src/components/pr/pr-merge-panel.tsx +++ b/apps/web/src/components/pr/pr-merge-panel.tsx @@ -28,6 +28,14 @@ import { DialogDescription, DialogFooter, } from "@/components/ui/dialog"; +import { + Sheet, + SheetContent, + SheetHeader, + SheetTitle, + SheetDescription, + SheetFooter, +} from "@/components/ui/sheet"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { mergePullRequest, @@ -40,6 +48,8 @@ import { } from "@/app/(app)/repos/[owner]/[repo]/pulls/pr-actions"; import { useMutationEvents } from "@/components/shared/mutation-event-provider"; import { useQueryClient } from "@tanstack/react-query"; +import { PRChecksPanel } from "@/components/pr/pr-checks-panel"; +import type { CheckStatus } from "@/lib/github"; interface PRMergePanelProps { owner: string; @@ -61,6 +71,7 @@ interface PRMergePanelProps { canTriage?: boolean; isAuthor?: boolean; branchBehindBase?: boolean; + checkStatus?: CheckStatus; } const mergeMethodLabels: Record = { @@ -98,6 +109,7 @@ export function PRMergePanel({ canTriage = true, isAuthor = false, branchBehindBase = false, + checkStatus, }: PRMergePanelProps) { const hasPermission = canTriage || isAuthor; const availableMethods: MergeMethod[] = [ @@ -111,8 +123,8 @@ export function PRMergePanel({ const { emit } = useMutationEvents(); const queryClient = useQueryClient(); const [method, setMethod] = useState(availableMethods[0] ?? "merge"); - const [dropdownOpen, setDropdownOpen] = useState(false); - const [squashDialogOpen, setSquashDialogOpen] = useState(false); + const [mergeSheetOpen, setMergeSheetOpen] = useState(false); + const [sheetMethodDropdownOpen, setSheetMethodDropdownOpen] = useState(false); const [commitTitle, setCommitTitle] = useState(""); const [commitMessage, setCommitMessage] = useState(""); const [isPending, startTransition] = useTransition(); @@ -126,8 +138,8 @@ export function PRMergePanel({ const [isMerged, setIsMerged] = useState(false); const [isGenerating, setIsGenerating] = useState(false); const [toolsDropdownOpen, setToolsDropdownOpen] = useState(false); - const dropdownRef = useRef(null); const toolsDropdownRef = useRef(null); + const sheetMethodDropdownRef = useRef(null); const isOpen = state === "open" && !merged && !isMerged; const showUpdateBranch = @@ -137,13 +149,13 @@ export function PRMergePanel({ const canMarkReady = isOpen && (canWrite || isAuthor) && draft; useClickOutside( - dropdownRef, - useCallback(() => setDropdownOpen(false), []), + toolsDropdownRef, + useCallback(() => setToolsDropdownOpen(false), []), ); useClickOutside( - toolsDropdownRef, - useCallback(() => setToolsDropdownOpen(false), []), + sheetMethodDropdownRef, + useCallback(() => setSheetMethodDropdownOpen(false), []), ); const handleFixWithGhost = () => { @@ -233,14 +245,14 @@ export function PRMergePanel({ ); if (res.error) { setMergeError(res.error); - if (!squashDialogOpen) { + if (!mergeSheetOpen) { setMergeErrorDialogOpen(true); } } else { setMergeError(null); emit({ type: "pr:merged", owner, repo, number: pullNumber }); invalidatePRQueries(); - setSquashDialogOpen(false); + setMergeSheetOpen(false); setIsMerged(true); router.refresh(); } @@ -249,17 +261,17 @@ export function PRMergePanel({ const handleMergeClick = () => { setMergeError(null); - if (method === "squash") { - setCommitTitle(`${prTitle} (#${pullNumber})`); - setCommitMessage(""); - setSquashDialogOpen(true); - } else { - doMerge(method); - } + setCommitTitle(`${prTitle} (#${pullNumber})`); + setCommitMessage(""); + setMergeSheetOpen(true); }; - const handleSquashConfirm = () => { - doMerge("squash", commitTitle || undefined, commitMessage || undefined); + const handleConfirmMerge = () => { + if (method === "squash") { + doMerge("squash", commitTitle || undefined, commitMessage || undefined); + } else { + doMerge(method, commitTitle || undefined, commitMessage || undefined); + } }; const handleClose = () => { @@ -409,178 +421,47 @@ export function PRMergePanel({ return ( <>
- {/* Merge button with dropdown */} + {/* Merge button */} {canWrite && ( -
-
- - - -
- - {dropdownOpen && ( -
- {availableMethods.map((m) => { - const disabled = - mergeable === false; - return ( - - ); - })} - {mergeable === false && ( - <> -
- - - - )} -
+ )} {/* Tools dropdown */} @@ -793,66 +674,83 @@ export function PRMergePanel({ - {/* Squash merge dialog */} - - - - - Squash and merge - - - All commits will be squashed into a single - commit. - - - {mergeable === false && ( -
- -
-

- This branch has merge - conflicts -

-

- Resolve conflicts before - merging, or let Ghost fix - them. -

+ {/* Merge sheet */} + + + + + {prTitle} + + + {mergeMethodLabels[method].description} into{" "} + + {baseBranch} + + + +
+ {checkStatus && checkStatus.total > 0 && ( + + )} + {mergeable === false && ( +
+ +
+

+ This branch has + merge conflicts +

+

+ Resolve conflicts + before merging, or + let Ghost fix them. +

+
+
- -
- )} - {mergeError && ( -
- -
-

- Merge failed -

-

- {mergeError} -

+ )} + {mergeError && ( +
+ +
+

+ Merge failed +

+

+ {mergeError} +

+
+
- -
- )} -
+ )}
- + - + +
+ {sheetMethodDropdownOpen && ( +
+ {availableMethods.map( + (m) => ( + + ), + )} +
)} - Confirm squash and merge - - - -
+
+ + + {/* Action error dialog */}