@@ -120,13 +120,24 @@ export const PROverview = memo(function PROverview() {
120120
121121 // Action loading states
122122 const [ closingPR , setClosingPR ] = useState ( false ) ;
123+ const [ reopeningPR , setReopeningPR ] = useState ( false ) ;
124+ const [ deletingBranch , setDeletingBranch ] = useState ( false ) ;
125+ const [ branchDeleted , setBranchDeleted ] = useState ( false ) ;
123126 const [ convertingToDraft , setConvertingToDraft ] = useState ( false ) ;
124127 const [ markingReady , setMarkingReady ] = useState ( false ) ;
125128 const [ assigningSelf , setAssigningSelf ] = useState ( false ) ;
126129
127- // Repo permissions - check if user can push (merge) and if repo is archived
130+ // Viewer permissions from GraphQL (more reliable than REST)
131+ const [ viewerPermission , setViewerPermission ] = useState < string | null > ( null ) ;
132+
133+ // Repo permissions - use GraphQL viewerPermission as primary source
128134 const isArchived = pr . base ?. repo ?. archived ?? false ;
129- const canPush = pr . base ?. repo ?. permissions ?. push ?? false ;
135+ // WRITE, MAINTAIN, or ADMIN permissions allow merging
136+ const canPush =
137+ viewerPermission === "ADMIN" ||
138+ viewerPermission === "MAINTAIN" ||
139+ viewerPermission === "WRITE" ||
140+ pr . base ?. repo ?. permissions ?. push === true ;
130141 const canMergeRepo = canWrite && canPush && ! isArchived ;
131142
132143 // Reviewers and Assignees state
@@ -192,7 +203,7 @@ export const PROverview = memo(function PROverview() {
192203 conversationData ,
193204 commitsData ,
194205 timelineData ,
195- reviewThreadsData ,
206+ reviewThreadsResult ,
196207 ] = await Promise . all ( [
197208 github
198209 . getPRReviews ( owner , repo , pr . number )
@@ -220,7 +231,10 @@ export const PROverview = memo(function PROverview() {
220231 . catch ( ( ) => [ ] as TimelineEvent [ ] ) ,
221232 github
222233 . getReviewThreads ( owner , repo , pr . number )
223- . catch ( ( ) => [ ] as ReviewThread [ ] ) ,
234+ . catch ( ( ) => ( {
235+ threads : [ ] as ReviewThread [ ] ,
236+ viewerPermission : null ,
237+ } ) ) ,
224238 ] ) ;
225239
226240 setReviews ( reviewsData ) ;
@@ -229,7 +243,8 @@ export const PROverview = memo(function PROverview() {
229243 setConversation ( conversationData ) ;
230244 setCommits ( commitsData ) ;
231245 setTimeline ( timelineData ) ;
232- setReviewThreads ( reviewThreadsData ) ;
246+ setReviewThreads ( reviewThreadsResult . threads ) ;
247+ setViewerPermission ( reviewThreadsResult . viewerPermission ) ;
233248 } finally {
234249 setLoading ( false ) ;
235250 }
@@ -389,6 +404,42 @@ export const PROverview = memo(function PROverview() {
389404 }
390405 } , [ github , owner , repo , pr . number , refetchPR ] ) ;
391406
407+ const handleReopenPR = useCallback ( async ( ) => {
408+ setReopeningPR ( true ) ;
409+ try {
410+ await github . reopenPR ( owner , repo , pr . number ) ;
411+ await refetchPR ( ) ;
412+ } catch ( error ) {
413+ console . error ( "Failed to reopen PR:" , error ) ;
414+ } finally {
415+ setReopeningPR ( false ) ;
416+ }
417+ } , [ github , owner , repo , pr . number , refetchPR ] ) ;
418+
419+ const handleDeleteBranch = useCallback ( async ( ) => {
420+ if (
421+ ! window . confirm (
422+ `Are you sure you want to delete the branch "${ pr . head . ref } "?`
423+ )
424+ ) {
425+ return ;
426+ }
427+
428+ setDeletingBranch ( true ) ;
429+ try {
430+ await github . deleteBranch (
431+ pr . head . repo ?. owner ?. login ?? owner ,
432+ pr . head . repo ?. name ?? repo ,
433+ pr . head . ref
434+ ) ;
435+ setBranchDeleted ( true ) ;
436+ } catch ( error ) {
437+ console . error ( "Failed to delete branch:" , error ) ;
438+ } finally {
439+ setDeletingBranch ( false ) ;
440+ }
441+ } , [ github , owner , repo , pr . head . ref , pr . head . repo ] ) ;
442+
392443 const handleRequestReviewer = useCallback (
393444 async ( login : string ) => {
394445 try {
@@ -857,6 +908,67 @@ export const PROverview = memo(function PROverview() {
857908 </ >
858909 ) }
859910
911+ { /* Closed with unmerged commits - show for closed, unmerged PRs */ }
912+ { pr . state === "closed" && ! pr . merged && (
913+ < div className = "border border-border rounded-md overflow-hidden" >
914+ < div className = "flex items-start gap-3 p-4 bg-card/30" >
915+ < div className = "p-2 rounded-full bg-purple-500/10 text-purple-400" >
916+ < GitBranch className = "w-5 h-5" />
917+ </ div >
918+ < div className = "flex-1 min-w-0" >
919+ < h3 className = "font-semibold" >
920+ Closed with unmerged commits
921+ </ h3 >
922+ < p className = "text-sm text-muted-foreground mt-1" >
923+ This pull request is closed, but the{ " " }
924+ < code className = "px-1.5 py-0.5 bg-blue-500/20 text-blue-300 rounded text-xs" >
925+ { pr . head . ref }
926+ </ code > { " " }
927+ branch has unmerged commits.
928+ </ p >
929+ </ div >
930+ { canMergeRepo && ! branchDeleted && (
931+ < button
932+ onClick = { handleDeleteBranch }
933+ disabled = { deletingBranch }
934+ className = "shrink-0 px-4 py-2 border border-border text-sm font-medium rounded-md hover:bg-muted/50 transition-colors disabled:opacity-50"
935+ >
936+ { deletingBranch ? (
937+ < span className = "flex items-center gap-2" >
938+ < Loader2 className = "w-4 h-4 animate-spin" />
939+ Deleting...
940+ </ span >
941+ ) : (
942+ "Delete branch"
943+ ) }
944+ </ button >
945+ ) }
946+ { branchDeleted && (
947+ < span className = "shrink-0 px-4 py-2 text-sm text-green-400 flex items-center gap-2" >
948+ < Check className = "w-4 h-4" />
949+ Branch deleted
950+ </ span >
951+ ) }
952+ </ div >
953+ { canMergeRepo && (
954+ < div className = "px-4 py-3 border-t border-border bg-card/10 flex items-center justify-end" >
955+ < button
956+ onClick = { handleReopenPR }
957+ disabled = { reopeningPR }
958+ className = "flex items-center gap-2 px-4 py-2 bg-green-600 text-white text-sm font-medium rounded-md hover:bg-green-700 transition-colors disabled:opacity-50"
959+ >
960+ { reopeningPR ? (
961+ < Loader2 className = "w-4 h-4 animate-spin" />
962+ ) : (
963+ < GitPullRequest className = "w-4 h-4" />
964+ ) }
965+ { reopeningPR ? "Reopening..." : "Reopen pull request" }
966+ </ button >
967+ </ div >
968+ ) }
969+ </ div >
970+ ) }
971+
860972 { /* Add a comment - only show when user can write (comments allowed even without push) */ }
861973 { canWrite ? (
862974 < div className = "flex gap-3" >
0 commit comments