Skip to content

feat: Filter PRs by status in LatestMergedPRs#427

Closed
Aditya8369 wants to merge 2 commits into
PRODHOSH:mainfrom
Aditya8369:feat/filter-prs
Closed

feat: Filter PRs by status in LatestMergedPRs#427
Aditya8369 wants to merge 2 commits into
PRODHOSH:mainfrom
Aditya8369:feat/filter-prs

Conversation

@Aditya8369

@Aditya8369 Aditya8369 commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Summary

I have implemented the filter dropdown for Pull Request

Related Issue

Closes #295

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Refactor
  • Chore / dependency update

Changes Made

  • GraphQL & REST integration: Modified fetchMergedPRs in profile-data.ts to fetch all states of pull requests

Checklist

  • I was assigned to the issue before opening this PR
  • My branch is up to date with main
  • Code works locally and I have tested it
  • No console.log left in src/
  • If schema changed — both schema.sql and a new migration file are included
  • If this is a UI change — I read DESIGN.md and followed the design system (colors, spacing, typography, components)
  • Docs updated if needed
  • PR title follows Conventional Commits format (feat:, fix:, docs:, etc.)
  • This PR description is written in my own words

Summary by CodeRabbit

  • New Features

    • Added filtering for pull requests by merged, open, or closed status.
    • Added status-aware date labels and empty-state messages.
    • Expanded recent pull request retrieval to include multiple statuses when available.
  • Bug Fixes

    • Improved date formatting resilience when pull request dates are invalid.
    • Added fallback retrieval paths when primary pull request data sources are unavailable.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

PR retrieval now includes open, closed, and merged states, with GraphQL and REST fallback paths. The shared PR type carries state metadata, and LatestMergedPRs provides status filtering, state-specific date labels, and empty-state rendering.

Changes

PR status retrieval and presentation

Layer / File(s) Summary
Status-aware PR data retrieval
src/types/index.ts, src/lib/github.ts, src/lib/profile-data.ts
MergedPR now includes state metadata; token-authenticated retrieval searches PRs across states through GraphQL with REST fallbacks.
Filtered PR presentation
src/components/profile/LatestMergedPRs.tsx
Adds merged/open/closed filtering, state-specific date labels, and an empty-state message.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ProfileData
  participant githubGraphQL
  participant GitHubAPI
  participant LatestMergedPRs
  ProfileData->>githubGraphQL: Search PRs across states
  githubGraphQL->>GitHubAPI: Submit GraphQL request
  GitHubAPI-->>ProfileData: Return PR nodes or failure
  ProfileData->>GitHubAPI: Search merged, open, and closed PRs through REST fallback
  GitHubAPI-->>ProfileData: Return mapped MergedPR records
  ProfileData-->>LatestMergedPRs: Provide PRs with state
  LatestMergedPRs->>LatestMergedPRs: Filter and render selected state
Loading

Possibly related PRs

Suggested reviewers: PRODHOSH

Poem

A bunny sorts PRs by state,
Merged, open, closed in a neat little crate.
GraphQL hops, REST follows near,
Dates bloom with labels clear.
The dropdown twitches—what a treat! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately describes the main change: filtering PRs by status in LatestMergedPRs.
Linked Issues check ✅ Passed The PR implements the requested Merged/Open/Closed filter and updates data fetching to retrieve all PR states.
Out of Scope Changes check ✅ Passed The changes appear scoped to the PR status filter feature and supporting data/model updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Biome (2.5.1)
src/lib/profile-data.ts

File contains syntax errors that prevent linting: Line 332: Illegal use of an export declaration not at the top level; Line 346: Illegal use of an export declaration not at the top level; Line 373: Illegal use of an export declaration not at the top level; Line 396: expected } but instead the file ends

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/lib/profile-data.ts

Parsing error: '}' expected.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Welcome to OSSfolio, @Aditya8369! 🎉

Thank you for opening this pull request and contributing to the open-source community! 🚀

To ensure a smooth review process, please make sure you have:

  • Checked that your changes work locally and compile cleanly.
  • Followed the guidelines outlined in CONTRIBUTING.md.
  • Linked your PR to an open issue (e.g. Closes #ISSUE_NUMBER).

We will review your PR as soon as possible. Happy coding! 💻✨

@github-actions github-actions Bot added frontend Related to UI / Next.js backend Supabase / API / database UI Visual / design changes labels Jul 11, 2026
@PRODHOSH PRODHOSH self-requested a review July 12, 2026 01:39
@PRODHOSH PRODHOSH added ELUSOC ELUSOC project submission completed ADVENTURER Intermediate — 25 pts labels Jul 12, 2026
@PRODHOSH

Copy link
Copy Markdown
Owner

fix the build error here @Aditya8369

@PRODHOSH PRODHOSH self-requested a review July 12, 2026 01:42

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/profile-data.ts (1)

295-325: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Remove dead old implementation left inside catch block — causes syntax error.

The old fetchMergedPRs body (lines 296–324) was not removed when the new REST fallback was added. It now sits inside the catch block at line 295. Tracing brace depth: the } at line 325 closes the catch block, but the function opened at line 181 is never closed. The GitHubUser interface at line 332 would be parsed as nested inside fetchMergedPRs, which is invalid TypeScript and will fail to compile.

Additionally, the dead code returns objects without the required state property, violating the updated MergedPR interface.

🐛 Proposed fix: remove dead code and close function properly
     return [...merged, ...open, ...closed];
-  } catch {
-  const query = `search/issues?q=author:${encodeURIComponent(username)}+type:pr+is:merged&sort=updated&order=desc&per_page=${limit}`;
-  try {
-    const res = await fetchWithTimeout(
-      `https://api.github.com/${query}`,
-      {
-        headers: { Accept: "application/vnd.github.v3+json" },
-        cache: "no-store",
-      },
-      10_000
-    );
-
-    if (!res.ok) {
-      await throwIfRateLimited(res);
-      return [];
-    }
-    const json = await res.json();
-    if (!Array.isArray(json.items)) return [];
-    return json.items.map((item: any) => ({
-      title: item.title,
-      url: item.html_url,
-      repoName: item.repository_url.split('/').slice(-1)[0],
-      mergedAt: item.closed_at,
-    }));
-  } catch (e) {
-    // A rate limit must not be flattened into "this user has none" — the result is
-    // persisted now, so that would cache an empty list as fresh for a full hour.
-    if (e instanceof Error && e.message === "RateLimit") throw e;
-    return [];
-  }
+  } catch (e) {
+    if (e instanceof Error && e.message === "RateLimit") throw e;
+    return [];
   }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/profile-data.ts` around lines 295 - 325, Remove the obsolete GitHub
REST fallback implementation nested inside the catch block of fetchMergedPRs,
then add the missing closing brace for fetchMergedPRs before the GitHubUser
interface. Keep the active implementation and its rate-limit handling unchanged,
and eliminate the dead mapping that omits the required MergedPR.state field.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/profile/LatestMergedPRs.tsx`:
- Line 56: In the onChange handler for LatestMergedPRs, replace the any
assertion on e.target.value with an assertion to the component’s defined
filter-value union type, preserving the existing setFilter update while
retaining compile-time validation of allowed values.

In `@src/lib/profile-data.ts`:
- Line 263: Update the REST fallback fetch options in fetchForQuery to use
cache: "no-store" instead of next: { revalidate: 3600 }, matching the existing
convention in fetchGitHubUser and fetchGitHubRepos so the database snapshot
remains the sole freshness source.
- Around line 256-294: Restore rate-limit propagation in fetchForQuery and the
surrounding fetchMergedPRs flow: call throwIfRateLimited(res) before returning
[] for non-OK responses, re-throw errors whose message is "RateLimit" instead of
swallowing them in both catch blocks, and preserve empty-list behavior for other
failures so syncProfileSnapshot’s rateLimited guard can detect throttling.

---

Outside diff comments:
In `@src/lib/profile-data.ts`:
- Around line 295-325: Remove the obsolete GitHub REST fallback implementation
nested inside the catch block of fetchMergedPRs, then add the missing closing
brace for fetchMergedPRs before the GitHubUser interface. Keep the active
implementation and its rate-limit handling unchanged, and eliminate the dead
mapping that omits the required MergedPR.state field.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4835715f-56e1-436d-b6e6-8a55bfaa59de

📥 Commits

Reviewing files that changed from the base of the PR and between 99e65a8 and e190f25.

📒 Files selected for processing (4)
  • src/components/profile/LatestMergedPRs.tsx
  • src/lib/github.ts
  • src/lib/profile-data.ts
  • src/types/index.ts

</h2>
<select
value={filter}
onChange={(e) => setFilter(e.target.value as any)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Replace as any with a proper type assertion.

Using as any bypasses type checking on the select value. Cast directly to the union type to catch invalid values at compile time.

🔧 Proposed fix
-          onChange={(e) => setFilter(e.target.value as any)}
+          onChange={(e) => setFilter(e.target.value as 'merged' | 'open' | 'closed')}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onChange={(e) => setFilter(e.target.value as any)}
onChange={(e) => setFilter(e.target.value as 'merged' | 'open' | 'closed')}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/profile/LatestMergedPRs.tsx` at line 56, In the onChange
handler for LatestMergedPRs, replace the any assertion on e.target.value with an
assertion to the component’s defined filter-value union type, preserving the
existing setFilter update while retaining compile-time validation of allowed
values.

Comment thread src/lib/profile-data.ts
Comment on lines +256 to +294
const fetchForQuery = async (q: string, state: "open" | "closed" | "merged"): Promise<MergedPR[]> => {
const query = `search/issues?q=${q}&sort=updated&order=desc&per_page=${limit}`;
try {
const res = await fetchWithTimeout(
`https://api.github.com/${query}`,
{
headers: { Accept: "application/vnd.github.v3+json" },
next: { revalidate: 3600 },
},
10_000
);
if (!res.ok) return [];
const json = await res.json();
if (!Array.isArray(json.items)) return [];
return json.items.map((item: any) => {
let date = item.created_at;
if (state === "merged" || state === "closed") {
date = item.closed_at || item.created_at;
}
return {
title: item.title,
url: item.html_url,
repoName: item.repository_url.split("/").slice(-1)[0],
mergedAt: date,
state,
};
});
} catch {
return [];
}
};

try {
const [merged, open, closed] = await Promise.all([
fetchForQuery(`author:${u}+type:pr+is:merged`, "merged"),
fetchForQuery(`author:${u}+type:pr+is:open`, "open"),
fetchForQuery(`author:${u}+type:pr+is:closed+is:unmerged`, "closed"),
]);
return [...merged, ...open, ...closed];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Restore rate-limit detection in the REST fallback.

The new fetchForQuery helper silently swallows all errors (catch { return []; }), including rate-limit responses. The old code called throwIfRateLimited(res) on non-OK responses and re-threw "RateLimit" errors so the caller could distinguish "no PRs" from "API throttled." Since results are persisted by the snapshot sync, a swallowed rate limit would cache an empty list as fresh.

Two changes are needed:

  1. In fetchForQuery: call throwIfRateLimited(res) when !res.ok, and re-throw "RateLimit" in the catch block.
  2. In the outer try/catch (line 288): re-throw "RateLimit" so it propagates to syncProfileSnapshot's rateLimited guard.
🔒 Proposed fix for rate-limit handling
 const fetchForQuery = async (q: string, state: "open" | "closed" | "merged"): Promise<MergedPR[]> => {
     const query = `search/issues?q=${q}&sort=updated&order=desc&per_page=${limit}`;
     try {
       const res = await fetchWithTimeout(
         `https://api.github.com/${query}`,
         {
           headers: { Accept: "application/vnd.github.v3+json" },
           cache: "no-store",
         },
         10_000
       );
-      if (!res.ok) return [];
+      if (!res.ok) {
+        await throwIfRateLimited(res);
+        return [];
+      }
       const json = await res.json();
       if (!Array.isArray(json.items)) return [];
       return json.items.map((item: any) => {
         let date = item.created_at;
         if (state === "merged" || state === "closed") {
           date = item.closed_at || item.created_at;
         }
         return {
           title: item.title,
           url: item.html_url,
           repoName: item.repository_url.split("/").slice(-1)[0],
           mergedAt: date,
           state,
         };
       });
-    } catch {
+    } catch (e) {
+      if (e instanceof Error && e.message === "RateLimit") throw e;
       return [];
     }
   };

   try {
     const [merged, open, closed] = await Promise.all([
       fetchForQuery(`author:${u}+type:pr+is:merged`, "merged"),
       fetchForQuery(`author:${u}+type:pr+is:open`, "open"),
       fetchForQuery(`author:${u}+type:pr+is:closed+is:unmerged`, "closed"),
     ]);
     return [...merged, ...open, ...closed];
-  } catch {
+  } catch (e) {
+    if (e instanceof Error && e.message === "RateLimit") throw e;
     return [];
   }
 }

Based on learnings, fetchMergedPRs is expected to detect rate limiting reliably for the snapshot sync's rateLimited guard.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const fetchForQuery = async (q: string, state: "open" | "closed" | "merged"): Promise<MergedPR[]> => {
const query = `search/issues?q=${q}&sort=updated&order=desc&per_page=${limit}`;
try {
const res = await fetchWithTimeout(
`https://api.github.com/${query}`,
{
headers: { Accept: "application/vnd.github.v3+json" },
next: { revalidate: 3600 },
},
10_000
);
if (!res.ok) return [];
const json = await res.json();
if (!Array.isArray(json.items)) return [];
return json.items.map((item: any) => {
let date = item.created_at;
if (state === "merged" || state === "closed") {
date = item.closed_at || item.created_at;
}
return {
title: item.title,
url: item.html_url,
repoName: item.repository_url.split("/").slice(-1)[0],
mergedAt: date,
state,
};
});
} catch {
return [];
}
};
try {
const [merged, open, closed] = await Promise.all([
fetchForQuery(`author:${u}+type:pr+is:merged`, "merged"),
fetchForQuery(`author:${u}+type:pr+is:open`, "open"),
fetchForQuery(`author:${u}+type:pr+is:closed+is:unmerged`, "closed"),
]);
return [...merged, ...open, ...closed];
const fetchForQuery = async (q: string, state: "open" | "closed" | "merged"): Promise<MergedPR[]> => {
const query = `search/issues?q=${q}&sort=updated&order=desc&per_page=${limit}`;
try {
const res = await fetchWithTimeout(
`https://api.github.com/${query}`,
{
headers: { Accept: "application/vnd.github.v3+json" },
cache: "no-store",
},
10_000
);
if (!res.ok) {
await throwIfRateLimited(res);
return [];
}
const json = await res.json();
if (!Array.isArray(json.items)) return [];
return json.items.map((item: any) => {
let date = item.created_at;
if (state === "merged" || state === "closed") {
date = item.closed_at || item.created_at;
}
return {
title: item.title,
url: item.html_url,
repoName: item.repository_url.split("/").slice(-1)[0],
mergedAt: date,
state,
};
});
} catch (e) {
if (e instanceof Error && e.message === "RateLimit") throw e;
return [];
}
};
try {
const [merged, open, closed] = await Promise.all([
fetchForQuery(`author:${u}+type:pr+is:merged`, "merged"),
fetchForQuery(`author:${u}+type:pr+is:open`, "open"),
fetchForQuery(`author:${u}+type:pr+is:closed+is:unmerged`, "closed"),
]);
return [...merged, ...open, ...closed];
} catch (e) {
if (e instanceof Error && e.message === "RateLimit") throw e;
return [];
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/profile-data.ts` around lines 256 - 294, Restore rate-limit
propagation in fetchForQuery and the surrounding fetchMergedPRs flow: call
throwIfRateLimited(res) before returning [] for non-OK responses, re-throw
errors whose message is "RateLimit" instead of swallowing them in both catch
blocks, and preserve empty-list behavior for other failures so
syncProfileSnapshot’s rateLimited guard can detect throttling.

Source: Learnings

Comment thread src/lib/profile-data.ts
`https://api.github.com/${query}`,
{
headers: { Accept: "application/vnd.github.v3+json" },
next: { revalidate: 3600 },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use cache: "no-store" instead of next: { revalidate: 3600 } in the REST fallback.

fetchForQuery uses next: { revalidate: 3600 }, but fetchGitHubUser (line 354) and fetchGitHubRepos (line 378) in the same file deliberately use cache: "no-store" with an explicit comment: "The snapshot TTL in the DB is now the single source of freshness. A second, independent Next Data Cache in front of it would let a 'stale' sync be served an hour-old response." The REST fallback should follow the same convention to avoid serving stale PR data.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/profile-data.ts` at line 263, Update the REST fallback fetch options
in fetchForQuery to use cache: "no-store" instead of next: { revalidate: 3600 },
matching the existing convention in fetchGitHubUser and fetchGitHubRepos so the
database snapshot remains the sole freshness source.

@PRODHOSH

Copy link
Copy Markdown
Owner

fix the build error and coderabbit comments @Aditya8369

@Aditya8369 Aditya8369 closed this Jul 15, 2026
@Aditya8369 Aditya8369 deleted the feat/filter-prs branch July 15, 2026 07:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ADVENTURER Intermediate — 25 pts backend Supabase / API / database ELUSOC ELUSOC project submission frontend Related to UI / Next.js in-progress UI Visual / design changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] Filter PRs by status in LatestMergedPRs

2 participants