From e4c9f6a37199f95fc2327097d4e076481df84e1a Mon Sep 17 00:00:00 2001 From: Kate Date: Sat, 7 Feb 2026 16:57:57 -0800 Subject: [PATCH 1/2] Fix 'is_merged' not being available on the Issue --- .../apps/github/api/internal/nodes/issue.py | 28 ++++++++++--------- .../mentorship/api/internal/nodes/module.py | 8 ++++-- .../api/internal/queries/mentorship.py | 3 +- .../unit/components/IssuesTable.test.tsx | 5 ++-- .../__tests__/unit/pages/IssuesPage.test.tsx | 2 +- .../pages/ModuleIssueDetailsPage.test.tsx | 2 +- .../[moduleKey]/issues/[issueId]/page.tsx | 2 +- frontend/src/components/IssuesTable.tsx | 2 +- 8 files changed, 29 insertions(+), 23 deletions(-) diff --git a/backend/apps/github/api/internal/nodes/issue.py b/backend/apps/github/api/internal/nodes/issue.py index 7f0c5be076..994e555031 100644 --- a/backend/apps/github/api/internal/nodes/issue.py +++ b/backend/apps/github/api/internal/nodes/issue.py @@ -2,13 +2,22 @@ import strawberry import strawberry_django -from django.db.models import Exists, OuterRef +from django.db.models import Prefetch from apps.github.api.internal.nodes.pull_request import PullRequestNode from apps.github.api.internal.nodes.user import UserNode from apps.github.models.issue import Issue from apps.github.models.pull_request import PullRequest +MERGED_PULL_REQUESTS_PREFETCH = Prefetch( + "pull_requests", + queryset=PullRequest.objects.filter( + state="closed", + merged_at__isnull=False, + ), + to_attr="merged_pull_requests", +) + @strawberry_django.type( Issue, @@ -47,20 +56,13 @@ def labels(self, root: Issue) -> list[str]: """Resolve label names for the issue.""" return [label.name for label in root.labels.all()] - @strawberry_django.field( - annotate={ - "is_merged": Exists( - PullRequest.objects.filter( - merged_at__isnull=False, - related_issues=OuterRef("pk"), - state="closed", - ) - ) - } - ) + @strawberry_django.field(prefetch_related=[MERGED_PULL_REQUESTS_PREFETCH]) def is_merged(self, root: Issue) -> bool: """Return True if this issue has at least one merged pull request.""" - return root.is_merged + merged = getattr(root, "merged_pull_requests", None) + if merged is None: + return False + return len(merged) > 0 @strawberry_django.field(prefetch_related=["participant_interests__user"]) def interested_users(self, root: Issue) -> list[UserNode]: diff --git a/backend/apps/mentorship/api/internal/nodes/module.py b/backend/apps/mentorship/api/internal/nodes/module.py index 56edf42a32..c5558c6e78 100644 --- a/backend/apps/mentorship/api/internal/nodes/module.py +++ b/backend/apps/mentorship/api/internal/nodes/module.py @@ -5,7 +5,7 @@ import strawberry from apps.common.utils import normalize_limit -from apps.github.api.internal.nodes.issue import IssueNode +from apps.github.api.internal.nodes.issue import MERGED_PULL_REQUESTS_PREFETCH, IssueNode from apps.github.api.internal.nodes.pull_request import PullRequestNode from apps.github.api.internal.nodes.user import UserNode from apps.github.models import Label @@ -86,7 +86,9 @@ def issues( return [] queryset = self.issues.select_related("repository", "author").prefetch_related( - "assignees", "labels" + "assignees", + "labels", + MERGED_PULL_REQUESTS_PREFETCH, ) if label and label != "all": @@ -120,7 +122,7 @@ def issue_by_number(self, number: int) -> IssueNode | None: """Return a single issue by its GitHub number within this module's linked issues.""" return ( self.issues.select_related("repository", "author") - .prefetch_related("assignees", "labels") + .prefetch_related("assignees", "labels", MERGED_PULL_REQUESTS_PREFETCH) .filter(number=number) .first() ) diff --git a/backend/apps/mentorship/api/internal/queries/mentorship.py b/backend/apps/mentorship/api/internal/queries/mentorship.py index 970b8b6622..901082e2d9 100644 --- a/backend/apps/mentorship/api/internal/queries/mentorship.py +++ b/backend/apps/mentorship/api/internal/queries/mentorship.py @@ -9,7 +9,7 @@ from django.db.models import Prefetch from apps.common.utils import normalize_limit -from apps.github.api.internal.nodes.issue import IssueNode +from apps.github.api.internal.nodes.issue import MERGED_PULL_REQUESTS_PREFETCH, IssueNode from apps.github.models import Label from apps.github.models.user import User as GithubUser from apps.mentorship.api.internal.nodes.mentee import MenteeNode @@ -124,6 +124,7 @@ def get_mentee_module_issues( "assignees", queryset=GithubUser.objects.only("id", "login", "name", "avatar_url"), ), + MERGED_PULL_REQUESTS_PREFETCH, ) .order_by("-created_at") ) diff --git a/frontend/__tests__/unit/components/IssuesTable.test.tsx b/frontend/__tests__/unit/components/IssuesTable.test.tsx index f9c805d183..f4c754a3c0 100644 --- a/frontend/__tests__/unit/components/IssuesTable.test.tsx +++ b/frontend/__tests__/unit/components/IssuesTable.test.tsx @@ -178,8 +178,9 @@ describe('', () => { it('renders Merged status badge when isMerged is true', () => { render() - const mergedBadges = screen.getAllByText('Merged') - expect(mergedBadges.length).toBeGreaterThan(0) + // Merged issues display with "Closed" text (purple badge) + const closedBadges = screen.getAllByText('Closed') + expect(closedBadges.length).toBeGreaterThan(0) }) it('defaults to Closed status for unknown states', () => { diff --git a/frontend/__tests__/unit/pages/IssuesPage.test.tsx b/frontend/__tests__/unit/pages/IssuesPage.test.tsx index 16d097b9d7..c3e901887a 100644 --- a/frontend/__tests__/unit/pages/IssuesPage.test.tsx +++ b/frontend/__tests__/unit/pages/IssuesPage.test.tsx @@ -192,7 +192,7 @@ describe('IssuesPage', () => { }) describe.each([ - { state: 'closed', isMerged: true, expectedText: 'Merged' }, + { state: 'closed', isMerged: true, expectedText: 'Closed' }, { state: 'closed', isMerged: false, expectedText: 'Closed' }, ])('issue states', ({ state, isMerged, expectedText }) => { it(`renders ${expectedText} issues correctly`, () => { diff --git a/frontend/__tests__/unit/pages/ModuleIssueDetailsPage.test.tsx b/frontend/__tests__/unit/pages/ModuleIssueDetailsPage.test.tsx index 76d1d36e1c..46c55e90cd 100644 --- a/frontend/__tests__/unit/pages/ModuleIssueDetailsPage.test.tsx +++ b/frontend/__tests__/unit/pages/ModuleIssueDetailsPage.test.tsx @@ -303,7 +303,7 @@ describe('ModuleIssueDetailsPage', () => { describe('issue states', () => { it.each([ - { state: 'closed', isMerged: true, expectedText: 'Merged' }, + { state: 'closed', isMerged: true, expectedText: 'Closed' }, { state: 'closed', isMerged: false, expectedText: 'Closed' }, { state: 'open', isMerged: false, expectedText: 'Open' }, ])('renders issue state as "$expectedText"', ({ state, isMerged, expectedText }) => { diff --git a/frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/issues/[issueId]/page.tsx b/frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/issues/[issueId]/page.tsx index 067dfc0da2..730450765d 100644 --- a/frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/issues/[issueId]/page.tsx +++ b/frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/issues/[issueId]/page.tsx @@ -115,7 +115,7 @@ const ModuleIssueDetailsPage = () => { issueStatusLabel = 'Open' } else if (issue.isMerged) { issueStatusClass = 'bg-[#8657E5] text-white' - issueStatusLabel = 'Merged' + issueStatusLabel = 'Closed' } else { issueStatusClass = 'bg-[#DA3633] text-white' issueStatusLabel = 'Closed' diff --git a/frontend/src/components/IssuesTable.tsx b/frontend/src/components/IssuesTable.tsx index 1e4805ca5e..6644856d7b 100644 --- a/frontend/src/components/IssuesTable.tsx +++ b/frontend/src/components/IssuesTable.tsx @@ -51,7 +51,7 @@ const IssuesTable: React.FC = ({ const getStatusBadge = (state: string, isMerged?: boolean) => { const statusMap: Record = { open: { text: 'Open', class: 'bg-[#238636]' }, - merged: { text: 'Merged', class: 'bg-[#8657E5]' }, + merged: { text: 'Closed', class: 'bg-[#8657E5]' }, closed: { text: 'Closed', class: 'bg-[#DA3633]' }, } From 8e772a20a4c74ab0a15495481f83ae9ec879305f Mon Sep 17 00:00:00 2001 From: Arkadii Yakovets Date: Sun, 8 Feb 2026 13:26:49 -0800 Subject: [PATCH 2/2] Update code --- backend/apps/github/api/internal/nodes/issue.py | 7 ++----- backend/apps/mentorship/api/internal/nodes/module.py | 6 +++++- backend/apps/mentorship/api/internal/queries/mentorship.py | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/backend/apps/github/api/internal/nodes/issue.py b/backend/apps/github/api/internal/nodes/issue.py index 994e555031..bfc7591b49 100644 --- a/backend/apps/github/api/internal/nodes/issue.py +++ b/backend/apps/github/api/internal/nodes/issue.py @@ -12,8 +12,8 @@ MERGED_PULL_REQUESTS_PREFETCH = Prefetch( "pull_requests", queryset=PullRequest.objects.filter( - state="closed", merged_at__isnull=False, + state="closed", ), to_attr="merged_pull_requests", ) @@ -59,10 +59,7 @@ def labels(self, root: Issue) -> list[str]: @strawberry_django.field(prefetch_related=[MERGED_PULL_REQUESTS_PREFETCH]) def is_merged(self, root: Issue) -> bool: """Return True if this issue has at least one merged pull request.""" - merged = getattr(root, "merged_pull_requests", None) - if merged is None: - return False - return len(merged) > 0 + return bool(getattr(root, "merged_pull_requests", None)) @strawberry_django.field(prefetch_related=["participant_interests__user"]) def interested_users(self, root: Issue) -> list[UserNode]: diff --git a/backend/apps/mentorship/api/internal/nodes/module.py b/backend/apps/mentorship/api/internal/nodes/module.py index c5558c6e78..ead2c53f37 100644 --- a/backend/apps/mentorship/api/internal/nodes/module.py +++ b/backend/apps/mentorship/api/internal/nodes/module.py @@ -122,7 +122,11 @@ def issue_by_number(self, number: int) -> IssueNode | None: """Return a single issue by its GitHub number within this module's linked issues.""" return ( self.issues.select_related("repository", "author") - .prefetch_related("assignees", "labels", MERGED_PULL_REQUESTS_PREFETCH) + .prefetch_related( + "assignees", + "labels", + MERGED_PULL_REQUESTS_PREFETCH, + ) .filter(number=number) .first() ) diff --git a/backend/apps/mentorship/api/internal/queries/mentorship.py b/backend/apps/mentorship/api/internal/queries/mentorship.py index 901082e2d9..3246db0044 100644 --- a/backend/apps/mentorship/api/internal/queries/mentorship.py +++ b/backend/apps/mentorship/api/internal/queries/mentorship.py @@ -119,11 +119,11 @@ def get_mentee_module_issues( module.issues.filter(assignees=github_user) .only("id", "number", "title", "state", "created_at", "url") .prefetch_related( - Prefetch("labels", queryset=Label.objects.only("id", "name")), Prefetch( "assignees", queryset=GithubUser.objects.only("id", "login", "name", "avatar_url"), ), + Prefetch("labels", queryset=Label.objects.only("id", "name")), MERGED_PULL_REQUESTS_PREFETCH, ) .order_by("-created_at")