From b00c9824cc8e452974f8baa91b60bf9947dc9d61 Mon Sep 17 00:00:00 2001 From: plind-dm Date: Wed, 13 May 2026 22:24:08 +0900 Subject: [PATCH 1/3] fix(miner-details): make filter button counts respect search query --- .../miners/MinerOpenDiscoveryIssuesByRepo.tsx | 36 +++++++++++++------ src/components/miners/MinerPRsTable.tsx | 12 +++++-- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/components/miners/MinerOpenDiscoveryIssuesByRepo.tsx b/src/components/miners/MinerOpenDiscoveryIssuesByRepo.tsx index de53ad82..7d59fe46 100644 --- a/src/components/miners/MinerOpenDiscoveryIssuesByRepo.tsx +++ b/src/components/miners/MinerOpenDiscoveryIssuesByRepo.tsx @@ -184,6 +184,20 @@ const getIssueCounts = (issues: RepositoryIssue[]) => ({ closed: issues.filter(isClosedIssue).length, }); +const applyIssueSearch = ( + issues: RepositoryIssue[], + search: string, +): RepositoryIssue[] => { + const q = search.trim().toLowerCase(); + if (!q) return issues; + return issues.filter( + (i) => + i.title.toLowerCase().includes(q) || + i.repositoryFullName.toLowerCase().includes(q) || + String(i.number).includes(q), + ); +}; + const applyIssueFilter = ( issues: RepositoryIssue[], filter: IssueFilter, @@ -195,15 +209,7 @@ const applyIssueFilter = ( if (filter === 'open') result = result.filter(isOpenIssue); else if (filter === 'solved') result = result.filter(isSolvedIssue); else if (filter === 'closed') result = result.filter(isClosedIssue); - const q = search.trim().toLowerCase(); - if (q) { - result = result.filter( - (i) => - i.title.toLowerCase().includes(q) || - i.repositoryFullName.toLowerCase().includes(q) || - String(i.number).includes(q), - ); - } + result = applyIssueSearch(result, search); return [...result].sort((a, b) => { let cmp = 0; if (sortField === 'number') cmp = a.number - b.number; @@ -409,8 +415,16 @@ const MinerOpenDiscoveryIssuesByRepo: React.FC< const mineTotalPages = Math.ceil(filteredMine.length / PAGE_SIZE); const otherTotalPages = Math.ceil(filteredOther.length / PAGE_SIZE); - const mineCounts = useMemo(() => getIssueCounts(mineIssues), [mineIssues]); - const otherCounts = useMemo(() => getIssueCounts(otherIssues), [otherIssues]); + // Count over the search scope (excluding the active status filter) so each + // button reflects what the user would see if they clicked it. + const mineCounts = useMemo( + () => getIssueCounts(applyIssueSearch(mineIssues, mineSearch)), + [mineIssues, mineSearch], + ); + const otherCounts = useMemo( + () => getIssueCounts(applyIssueSearch(otherIssues, otherSearch)), + [otherIssues, otherSearch], + ); const mineColumns: DataTableColumn[] = useMemo( diff --git a/src/components/miners/MinerPRsTable.tsx b/src/components/miners/MinerPRsTable.tsx index 18b796c1..f1895c14 100644 --- a/src/components/miners/MinerPRsTable.tsx +++ b/src/components/miners/MinerPRsTable.tsx @@ -200,10 +200,18 @@ const MinerPRsTable: React.FC = ({ githubId }) => { const totalPages = Math.ceil(sortedPRs.length / PAGE_SIZE); + // Count over the search + author scope (excluding the active status filter) + // so each button reflects what the user would see if they clicked it. const statusCounts = useMemo(() => { if (!prs) return { all: 0, open: 0, merged: 0, closed: 0 }; - return getPrStatusCounts(prs); - }, [prs]); + const scope = filterPrs(prs, { + author: selectedAuthor, + includeNumber: true, + searchQuery, + statusFilter: 'all', + }); + return getPrStatusCounts(scope); + }, [prs, selectedAuthor, searchQuery]); const hasFilters = Boolean(selectedAuthor) || From 3f2503bcb2f5a7a317a4e15265c4dab2e769d358 Mon Sep 17 00:00:00 2001 From: plind-dm Date: Wed, 13 May 2026 22:41:28 +0900 Subject: [PATCH 2/3] fix(miner-details): extend filter count fix to Overview and Repositories tabs --- .../miners/MinerRepositoriesTable.tsx | 44 ++++++++++--------- src/components/miners/MinerScoreBreakdown.tsx | 14 +++++- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/components/miners/MinerRepositoriesTable.tsx b/src/components/miners/MinerRepositoriesTable.tsx index 06d0a470..070e457a 100644 --- a/src/components/miners/MinerRepositoriesTable.tsx +++ b/src/components/miners/MinerRepositoriesTable.tsx @@ -158,27 +158,29 @@ const MinerRepositoriesTable: React.FC = ({ } }, [issueRepoStats, statusFilter]); - const repoStatusCounts = useMemo( - () => ({ - all: repoStats.length, - active: repoStats.filter((r) => !r.inactiveAt).length, - inactive: repoStats.filter((r) => !!r.inactiveAt).length, - recent: repoStats.filter(isRecentRepoStats).length, - stale: repoStats.filter(isStaleRepoStats).length, - }), - [repoStats], - ); - - const issueRepoStatusCounts = useMemo( - () => ({ - all: issueRepoStats.length, - active: issueRepoStats.filter((r) => !r.inactiveAt).length, - inactive: issueRepoStats.filter((r) => !!r.inactiveAt).length, - recent: issueRepoStats.filter(isRecentIssueRepoStats).length, - stale: issueRepoStats.filter(isStaleIssueRepoStats).length, - }), - [issueRepoStats], - ); + // Count over the search scope (excluding the active status filter) so each + // button reflects what the user would see if they clicked it. + const repoStatusCounts = useMemo(() => { + const scope = filterBySearch(repoStats, searchQuery); + return { + all: scope.length, + active: scope.filter((r) => !r.inactiveAt).length, + inactive: scope.filter((r) => !!r.inactiveAt).length, + recent: scope.filter(isRecentRepoStats).length, + stale: scope.filter(isStaleRepoStats).length, + }; + }, [repoStats, searchQuery]); + + const issueRepoStatusCounts = useMemo(() => { + const scope = filterBySearch(issueRepoStats, searchQuery); + return { + all: scope.length, + active: scope.filter((r) => !r.inactiveAt).length, + inactive: scope.filter((r) => !!r.inactiveAt).length, + recent: scope.filter(isRecentIssueRepoStats).length, + stale: scope.filter(isStaleIssueRepoStats).length, + }; + }, [issueRepoStats, searchQuery]); const filteredRepoStats = useMemo( () => filterBySearch(statusFilteredRepoStats, searchQuery), diff --git a/src/components/miners/MinerScoreBreakdown.tsx b/src/components/miners/MinerScoreBreakdown.tsx index cc3a767c..0b54f44e 100644 --- a/src/components/miners/MinerScoreBreakdown.tsx +++ b/src/components/miners/MinerScoreBreakdown.tsx @@ -1037,7 +1037,19 @@ const PrBreakdownView: React.FC<{ githubId: string }> = ({ githubId }) => { if (!isMobile) setIsMobileSearchOpen(false); }, [isMobile]); - const statusCounts = useMemo(() => getPrStatusCounts(prs ?? []), [prs]); + // Count over the search scope (excluding the active status filter) so each + // button reflects what the user would see if they clicked it. + const statusCounts = useMemo( + () => + getPrStatusCounts( + filterPrs(prs ?? [], { + searchQuery, + includeNumber: true, + statusFilter: 'all', + }), + ), + [prs, searchQuery], + ); const statusFilterTotal = useMemo(() => { switch (statusFilter) { From 131bb78805ac935c0462e63641c45c3c144d6871 Mon Sep 17 00:00:00 2001 From: plind-dm Date: Sun, 17 May 2026 10:48:24 +0900 Subject: [PATCH 3/3] fix(miner-details): unify PR search matcher to fix #-prefix count drift --- src/components/miners/MinerScoreBreakdown.tsx | 25 ++++++------------- src/utils/prTable.ts | 17 ++++++++----- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/components/miners/MinerScoreBreakdown.tsx b/src/components/miners/MinerScoreBreakdown.tsx index 0b54f44e..c9c1ba1d 100644 --- a/src/components/miners/MinerScoreBreakdown.tsx +++ b/src/components/miners/MinerScoreBreakdown.tsx @@ -1068,24 +1068,13 @@ const PrBreakdownView: React.FC<{ githubId: string }> = ({ githubId }) => { const filteredPrs = useMemo(() => { if (!prs) return []; - let list = [...filterPrs(prs, { statusFilter })].sort( - (a, b) => parseFloat(b.score || '0') - parseFloat(a.score || '0'), - ); - const q = searchQuery.trim().toLowerCase(); - if (q) { - list = list.filter((pr) => { - const title = (pr.pullRequestTitle || '').toLowerCase(); - const repo = (pr.repository || '').toLowerCase(); - const num = String(pr.pullRequestNumber); - return ( - title.includes(q) || - repo.includes(q) || - num.includes(q) || - `#${num}`.includes(q) - ); - }); - } - return list; + return [ + ...filterPrs(prs, { + searchQuery, + includeNumber: true, + statusFilter, + }), + ].sort((a, b) => parseFloat(b.score || '0') - parseFloat(a.score || '0')); }, [prs, statusFilter, searchQuery]); const paging = useMemo(() => { diff --git a/src/utils/prTable.ts b/src/utils/prTable.ts index 92429488..08d8dd3f 100644 --- a/src/utils/prTable.ts +++ b/src/utils/prTable.ts @@ -33,12 +33,17 @@ export const filterPrs = ( const normalizedQuery = searchQuery.trim().toLowerCase(); if (!normalizedQuery) return filtered; - return filtered.filter( - (pr) => - pr.pullRequestTitle?.toLowerCase().includes(normalizedQuery) || - pr.repository.toLowerCase().includes(normalizedQuery) || - (includeNumber && String(pr.pullRequestNumber).includes(normalizedQuery)), - ); + return filtered.filter((pr) => { + if (pr.pullRequestTitle?.toLowerCase().includes(normalizedQuery)) + return true; + if (pr.repository.toLowerCase().includes(normalizedQuery)) return true; + if (includeNumber) { + const num = String(pr.pullRequestNumber); + if (num.includes(normalizedQuery)) return true; + if (`#${num}`.includes(normalizedQuery)) return true; + } + return false; + }); }; export const paginateItems = (items: T[], page: number, pageSize: number) =>