@@ -397,6 +397,25 @@ const App = (() => {
397397 ${ reviewers }
398398 </div>
399399 </div>
400+ <div class="pr-actions">
401+ <button class="pr-action-btn" data-action="merge" data-pr-id="${ pr . id } " title="Merge PR">
402+ <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
403+ <path d="M5 3.25a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0zm0 9.5a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0zm8.25-6.5a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0z"/>
404+ <path d="M1.75 5.5v5a.75.75 0 001.5 0v-5a.75.75 0 00-1.5 0zm6.5-3.25a.75.75 0 000 1.5h1.5v2.5a2.25 2.25 0 01-2.25 2.25h-1a.75.75 0 000 1.5h1a3.75 3.75 0 003.75-3.75v-2.5h1.5a.75.75 0 000-1.5h-5z"/>
405+ </svg>
406+ </button>
407+ <button class="pr-action-btn" data-action="unassign" data-pr-id="${ pr . id } " title="Unassign">
408+ <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
409+ <path d="M10.5 5a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0zm.514 2.63a4 4 0 10-6.028 0A4.002 4.002 0 002 11.5V13a1 1 0 001 1h10a1 1 0 001-1v-1.5a4.002 4.002 0 00-2.986-3.87zM8 1a3 3 0 100 6 3 3 0 000-6zM3 11.5A3 3 0 016 8.5h4a3 3 0 013 3V13H3v-1.5z"/>
410+ <path d="M12.146 5.146a.5.5 0 01.708 0l2 2a.5.5 0 010 .708l-2 2a.5.5 0 01-.708-.708L13.293 8l-1.147-1.146a.5.5 0 010-.708z"/>
411+ </svg>
412+ </button>
413+ <button class="pr-action-btn" data-action="close" data-pr-id="${ pr . id } " title="Close PR">
414+ <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
415+ <path d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"/>
416+ </svg>
417+ </button>
418+ </div>
400419 </div>
401420 ` ;
402421 } ;
@@ -488,6 +507,118 @@ const App = (() => {
488507 hide ( emptyState ) ;
489508 }
490509 } ;
510+
511+ const handlePRAction = async ( action , prId ) => {
512+ // Find PR in all sections
513+ const allPRs = [
514+ ...state . pullRequests . incoming ,
515+ ...state . pullRequests . outgoing ,
516+ ...state . pullRequests . drafts
517+ ] ;
518+ const pr = allPRs . find ( p => p . id . toString ( ) === prId ) ;
519+ if ( ! pr ) return ;
520+
521+ const token = getStoredToken ( ) ;
522+ if ( ! token ) {
523+ showToast ( 'Please login to perform this action' , 'error' ) ;
524+ return ;
525+ }
526+
527+ try {
528+ let response ;
529+ const headers = {
530+ 'Authorization' : `token ${ token } ` ,
531+ 'Accept' : 'application/vnd.github.v3+json'
532+ } ;
533+
534+ switch ( action ) {
535+ case 'merge' :
536+ response = await fetch ( `${ CONFIG . API_BASE } /repos/${ pr . repository . full_name } /pulls/${ pr . number } /merge` , {
537+ method : 'PUT' ,
538+ headers : {
539+ ...headers ,
540+ 'Content-Type' : 'application/json'
541+ } ,
542+ body : JSON . stringify ( {
543+ commit_title : `Merge pull request #${ pr . number } from ${ pr . head . ref } ` ,
544+ commit_message : pr . title
545+ } )
546+ } ) ;
547+
548+ if ( response . ok ) {
549+ showToast ( 'PR merged successfully' , 'success' ) ;
550+ // Remove PR from state
551+ [ 'incoming' , 'outgoing' , 'drafts' ] . forEach ( section => {
552+ const index = state . pullRequests [ section ] . findIndex ( p => p . id . toString ( ) === prId ) ;
553+ if ( index !== - 1 ) {
554+ state . pullRequests [ section ] . splice ( index , 1 ) ;
555+ }
556+ } ) ;
557+ // Update the display
558+ updatePRSections ( ) ;
559+ } else {
560+ const error = await response . json ( ) ;
561+ showToast ( error . message || 'Failed to merge PR' , 'error' ) ;
562+ }
563+ break ;
564+
565+ case 'unassign' :
566+ response = await fetch ( `${ CONFIG . API_BASE } /repos/${ pr . repository . full_name } /issues/${ pr . number } /assignees` , {
567+ method : 'DELETE' ,
568+ headers : {
569+ ...headers ,
570+ 'Content-Type' : 'application/json'
571+ } ,
572+ body : JSON . stringify ( {
573+ assignees : pr . assignees ?. map ( a => a . login ) || [ ]
574+ } )
575+ } ) ;
576+
577+ if ( response . ok ) {
578+ showToast ( 'Unassigned from PR' , 'success' ) ;
579+ // Refresh the PR list
580+ updatePRSections ( ) ;
581+ } else {
582+ showToast ( 'Failed to unassign' , 'error' ) ;
583+ }
584+ break ;
585+
586+ case 'close' :
587+ response = await fetch ( `${ CONFIG . API_BASE } /repos/${ pr . repository . full_name } /pulls/${ pr . number } ` , {
588+ method : 'PATCH' ,
589+ headers : {
590+ ...headers ,
591+ 'Content-Type' : 'application/json'
592+ } ,
593+ body : JSON . stringify ( {
594+ state : 'closed'
595+ } )
596+ } ) ;
597+
598+ if ( response . ok ) {
599+ showToast ( 'PR closed' , 'success' ) ;
600+ // Remove PR from state
601+ [ 'incoming' , 'outgoing' , 'drafts' ] . forEach ( section => {
602+ const index = state . pullRequests [ section ] . findIndex ( p => p . id . toString ( ) === prId ) ;
603+ if ( index !== - 1 ) {
604+ state . pullRequests [ section ] . splice ( index , 1 ) ;
605+ }
606+ } ) ;
607+ // Update the display
608+ updatePRSections ( ) ;
609+ } else {
610+ const errorMsg = response . status === 403 ?
611+ 'Failed to close PR - Permission denied' :
612+ 'Failed to close PR' ;
613+ showToast ( errorMsg , 'error' ) ;
614+ }
615+ break ;
616+ }
617+ } catch ( error ) {
618+ console . error ( 'Error performing PR action:' , error ) ;
619+ showToast ( 'An error occurred' , 'error' ) ;
620+ }
621+ } ;
491622
492623 const handleKeyboardShortcuts = e => {
493624 if ( e . target . matches ( 'input, textarea' ) ) return ;
@@ -699,6 +830,16 @@ const App = (() => {
699830
700831 document . addEventListener ( 'keydown' , handleKeyboardShortcuts ) ;
701832
833+ // Add event delegation for PR action buttons
834+ document . addEventListener ( 'click' , ( e ) => {
835+ if ( e . target . closest ( '.pr-action-btn' ) ) {
836+ const btn = e . target . closest ( '.pr-action-btn' ) ;
837+ const action = btn . dataset . action ;
838+ const prId = btn . dataset . prId ;
839+ handlePRAction ( action , prId ) ;
840+ }
841+ } ) ;
842+
702843 // Check for OAuth callback
703844 if ( urlParams . get ( 'code' ) ) {
704845 handleOAuthCallback ( ) ;
0 commit comments