@@ -19,6 +19,7 @@ import { HeaderSection } from './components/isolated/header-section';
1919import { CiSection } from ' ./components/isolated/ci-section' ;
2020import { ReviewSection } from ' ./components/isolated/review-section' ;
2121import { SummarySection } from ' ./components/isolated/summary-section' ;
22+ import { MergeableSection } from ' ./components/isolated/mergeable-section' ;
2223
2324import {
2425 renderPrActionLabel ,
@@ -196,6 +197,44 @@ class IsolatedTemplate extends Component<typeof PrCard> {
196197 return !! this .latestPrReviewCommentEventInstance ;
197198 }
198199
200+ // ── Mergeability ──
201+ get isClosed() {
202+ let label = this .latestPrActionLabel ;
203+ return label === ' Closed' || label === ' Merged' ;
204+ }
205+
206+ get isDraft() {
207+ return this .latestPrActionLabel === ' Draft' ;
208+ }
209+
210+ get mergeBlockReasons(): string [] {
211+ if (this .isClosed ) return [];
212+ let reasons: string [] = [];
213+ if (this .isDraft ) {
214+ reasons .push (' This pull request is still a work in progress' );
215+ }
216+ let { ciItems } = this ;
217+ if (ciItems .some ((i ) => i .state === ' failure' )) {
218+ reasons .push (' Some checks were not successful' );
219+ } else if (ciItems .some ((i ) => i .state === ' in_progress' )) {
220+ reasons .push (' Some checks are still in progress' );
221+ }
222+ let reviewState = this .latestReviewState ;
223+ if (reviewState === ' changes_requested' ) {
224+ reasons .push (' Changes were requested by a reviewer' );
225+ } else if (reviewState !== ' approved' ) {
226+ reasons .push (
227+ ' At least 1 approving review is required by reviewers with write access' ,
228+ );
229+ }
230+ return reasons ;
231+ }
232+
233+ get isMergeable() {
234+ if (this .isClosed ) return false ;
235+ return this .mergeBlockReasons .length === 0 ;
236+ }
237+
199238 <template >
200239 <article class =' pr-card' >
201240 <HeaderSection
@@ -229,6 +268,12 @@ class IsolatedTemplate extends Component<typeof PrCard> {
229268 />
230269 </section >
231270
271+ <MergeableSection
272+ @ isMergeable ={{this .isMergeable }}
273+ @ isClosedOrMerged ={{this .isClosed }}
274+ @ blockReasons ={{this .mergeBlockReasons }}
275+ />
276+
232277 <SummarySection @ summary ={{this .prBodySummary }} />
233278 </div >
234279 </article >
@@ -446,6 +491,21 @@ class FittedTemplate extends Component<typeof PrCard> {
446491 return computeLatestReviewState (this .latestReviewByReviewer );
447492 }
448493
494+ // ── Mergeability ──
495+ get isClosed() {
496+ let label = this .latestPrActionLabel ;
497+ return label === ' Closed' || label === ' Merged' ;
498+ }
499+
500+ get isMergeBlocked() {
501+ if (this .isClosed ) return false ;
502+ if (this .latestPrActionLabel === ' Draft' ) return true ;
503+ if (this .ciItems .some ((i ) => i .state === ' failure' )) return true ;
504+ if (this .ciItems .some ((i ) => i .state === ' in_progress' )) return true ;
505+ if (this .latestReviewState !== ' approved' ) return true ;
506+ return false ;
507+ }
508+
449509 copyBranchName = async () => {
450510 let branchName = this .prBranchName ?.trim ();
451511 if (! branchName ) {
@@ -527,14 +587,29 @@ class FittedTemplate extends Component<typeof PrCard> {
527587 {{#if ( eq this . latestReviewState ' changes_requested' ) }}
528588 <div class =' review-status-row review-status-row--changes' >
529589 <span class =' review-status-label' >Changes Requested</span >
590+ {{#if this . isMergeBlocked }}
591+ <Pill class =' merge-blocked-pill' @ pillBackgroundColor =' #d73a49' >
592+ <: default ><span class =' merge-blocked-label' >Merge blocked</span ></: default >
593+ </Pill >
594+ {{/if }}
530595 </div >
531596 {{else if ( eq this . latestReviewState ' approved' ) }}
532597 <div class =' review-status-row review-status-row--approved' >
533598 <span class =' review-status-label' >Approved</span >
599+ {{#if this . isMergeBlocked }}
600+ <Pill class =' merge-blocked-pill' @ pillBackgroundColor =' #d73a49' >
601+ <: default ><span class =' merge-blocked-label' >Merge blocked</span ></: default >
602+ </Pill >
603+ {{/if }}
534604 </div >
535605 {{else }}
536606 <div class =' review-status-row review-status-row--pending' >
537607 <span class =' review-status-label' >Pending Review</span >
608+ {{#if this . isMergeBlocked }}
609+ <Pill class =' merge-blocked-pill' @ pillBackgroundColor =' #d73a49' >
610+ <: default ><span class =' merge-blocked-label' >Merge blocked</span ></: default >
611+ </Pill >
612+ {{/if }}
538613 </div >
539614 {{/if }}
540615
@@ -729,6 +804,7 @@ class FittedTemplate extends Component<typeof PrCard> {
729804 .review-status-row {
730805 display : flex ;
731806 align-items : center ;
807+ justify-content : space-between ;
732808 padding : var (--boxel-sp-xs ) var (--boxel-sp-sm );
733809 border-bottom : 1px solid var (--border , var (--boxel-border-color ));
734810 }
@@ -762,6 +838,15 @@ class FittedTemplate extends Component<typeof PrCard> {
762838 .review-status-row--approved .review-status-label {
763839 color : var (--chart-1 , #28a745 );
764840 }
841+ /* ── Merge blocked pill ── */
842+ .merge-blocked-pill {
843+ --boxel-pill-border-radius : 2em ;
844+ }
845+ .merge-blocked-label {
846+ font-size : 10px ;
847+ font-weight : 600 ;
848+ color : #fff ;
849+ }
765850 /* ── Summary ── */
766851 .summary-section {
767852 display : flex ;
0 commit comments