feat: add issues tracking page with filters and sorting#273
feat: add issues tracking page with filters and sorting#273isauravanand wants to merge 1 commit into
Conversation
✅ Deploy Preview for github-spy ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
📝 WalkthroughWalkthroughThis PR adds a new Issues page that fetches and displays GitHub issues with language, label, and sort order filters. It includes the page component with API integration and pagination, plus routing and navbar navigation to make it accessible from the app. ChangesIssues Page Feature
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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. Comment |
There was a problem hiding this comment.
🎉 Thank you @isauravanand for your contribution. Please make sure your PR follows https://github.com/GitMetricsLab/github_tracker/blob/main/CONTRIBUTING.md#-pull-request-guidelines
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/pages/Issues/Issues.tsx`:
- Around line 50-90: fetchIssues can race when multiple requests are in-flight;
create an AbortController ref (e.g., abortControllerRef) and before starting a
new fetch in fetchIssues abort any existing controller, then create a new
controller and pass its signal to fetch; in the catch block ignore abort errors
(check err.name === 'AbortError' or err instanceof DOMException) and only
setError for real failures; also abort any pending request in the useEffect
cleanup (return () => abortControllerRef.current?.abort()) so stale responses
never overwrite newer state.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: f6d52a1d-61db-4469-baea-c94533ed0b45
📒 Files selected for processing (3)
src/Routes/Router.tsxsrc/components/Navbar.tsxsrc/pages/Issues/Issues.tsx
| const fetchIssues = useCallback(async (currentPage: number, currentLanguage: string, currentTag: string, currentOrder: string) => { | ||
| setLoading(true); | ||
| setError(""); | ||
|
|
||
| try { | ||
| let q = "is:issue is:open"; | ||
| if (currentLanguage) { | ||
| q += ` language:${currentLanguage}`; | ||
| } | ||
| if (currentTag) { | ||
| q += ` label:"${currentTag}"`; | ||
| } | ||
|
|
||
| const response = await fetch( | ||
| `https://api.github.com/search/issues?q=${encodeURIComponent(q)}&sort=created&order=${currentOrder}&per_page=${ROWS_PER_PAGE}&page=${currentPage + 1}` | ||
| ); | ||
|
|
||
| if (!response.ok) { | ||
| if (response.status === 403) { | ||
| throw new Error("GitHub API rate limit exceeded."); | ||
| } | ||
| throw new Error("Failed to fetch data"); | ||
| } | ||
|
|
||
| const data = await response.json(); | ||
| setIssues(data.items); | ||
| setTotalIssues(data.total_count > 1000 ? 1000 : data.total_count); // GitHub limits search results to 1000 | ||
| } catch (err: unknown) { | ||
| if (err instanceof Error) { | ||
| setError(err.message || "Failed to fetch issues"); | ||
| } else { | ||
| setError("Failed to fetch issues"); | ||
| } | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| }, []); | ||
|
|
||
| useEffect(() => { | ||
| fetchIssues(page, language, tag, sortOrder); | ||
| }, [page, language, tag, sortOrder, fetchIssues]); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether request cancellation is implemented for Issues fetching.
rg -n --type=tsx -e 'AbortController' -e 'controller\.abort' -e 'signal' -e 'fetchIssues\(' src/pages/Issues/Issues.tsxRepository: GitMetricsLab/github_tracker
Length of output: 100
🏁 Script executed:
fd -i "issues.tsx" -o -i "issues.ts"Repository: GitMetricsLab/github_tracker
Length of output: 189
🏁 Script executed:
fd -type f "Issues" src/pages/Repository: GitMetricsLab/github_tracker
Length of output: 243
🏁 Script executed:
find src/pages/ -name "*Issues*" -type fRepository: GitMetricsLab/github_tracker
Length of output: 100
🏁 Script executed:
cat src/pages/Issues/Issues.tsxRepository: GitMetricsLab/github_tracker
Length of output: 7909
Prevent stale results from out-of-order fetch responses.
Multiple in-flight requests can race here; an older response can arrive last and overwrite the latest filter/page state. This happens because rapid filter/page/sort changes trigger new fetchIssues calls via the dependency array [page, language, tag, sortOrder, fetchIssues] without canceling the previous request.
💡 Suggested fix (abort previous request + ignore abort errors)
- const fetchIssues = useCallback(async (currentPage: number, currentLanguage: string, currentTag: string, currentOrder: string) => {
+ const fetchIssues = useCallback(
+ async (
+ currentPage: number,
+ currentLanguage: string,
+ currentTag: string,
+ currentOrder: string,
+ signal: AbortSignal
+ ) => {
setLoading(true);
setError("");
@@
- const response = await fetch(
- `https://api.github.com/search/issues?q=${encodeURIComponent(q)}&sort=created&order=${currentOrder}&per_page=${ROWS_PER_PAGE}&page=${currentPage + 1}`
- );
+ const response = await fetch(
+ `https://api.github.com/search/issues?q=${encodeURIComponent(q)}&sort=created&order=${currentOrder}&per_page=${ROWS_PER_PAGE}&page=${currentPage + 1}`,
+ { signal }
+ );
@@
const data = await response.json();
+ if (signal.aborted) return;
setIssues(data.items);
setTotalIssues(data.total_count > 1000 ? 1000 : data.total_count); // GitHub limits search results to 1000
} catch (err: unknown) {
+ if (err instanceof DOMException && err.name === "AbortError") return;
if (err instanceof Error) {
setError(err.message || "Failed to fetch issues");
} else {
setError("Failed to fetch issues");
}
} finally {
setLoading(false);
}
- }, []);
+ }, []);
@@
- useEffect(() => {
- fetchIssues(page, language, tag, sortOrder);
- }, [page, language, tag, sortOrder, fetchIssues]);
+ useEffect(() => {
+ const controller = new AbortController();
+ fetchIssues(page, language, tag, sortOrder, controller.signal);
+ return () => controller.abort();
+ }, [page, language, tag, sortOrder, fetchIssues]);📝 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.
| const fetchIssues = useCallback(async (currentPage: number, currentLanguage: string, currentTag: string, currentOrder: string) => { | |
| setLoading(true); | |
| setError(""); | |
| try { | |
| let q = "is:issue is:open"; | |
| if (currentLanguage) { | |
| q += ` language:${currentLanguage}`; | |
| } | |
| if (currentTag) { | |
| q += ` label:"${currentTag}"`; | |
| } | |
| const response = await fetch( | |
| `https://api.github.com/search/issues?q=${encodeURIComponent(q)}&sort=created&order=${currentOrder}&per_page=${ROWS_PER_PAGE}&page=${currentPage + 1}` | |
| ); | |
| if (!response.ok) { | |
| if (response.status === 403) { | |
| throw new Error("GitHub API rate limit exceeded."); | |
| } | |
| throw new Error("Failed to fetch data"); | |
| } | |
| const data = await response.json(); | |
| setIssues(data.items); | |
| setTotalIssues(data.total_count > 1000 ? 1000 : data.total_count); // GitHub limits search results to 1000 | |
| } catch (err: unknown) { | |
| if (err instanceof Error) { | |
| setError(err.message || "Failed to fetch issues"); | |
| } else { | |
| setError("Failed to fetch issues"); | |
| } | |
| } finally { | |
| setLoading(false); | |
| } | |
| }, []); | |
| useEffect(() => { | |
| fetchIssues(page, language, tag, sortOrder); | |
| }, [page, language, tag, sortOrder, fetchIssues]); | |
| const fetchIssues = useCallback( | |
| async ( | |
| currentPage: number, | |
| currentLanguage: string, | |
| currentTag: string, | |
| currentOrder: string, | |
| signal: AbortSignal | |
| ) => { | |
| setLoading(true); | |
| setError(""); | |
| try { | |
| let q = "is:issue is:open"; | |
| if (currentLanguage) { | |
| q += ` language:${currentLanguage}`; | |
| } | |
| if (currentTag) { | |
| q += ` label:"${currentTag}"`; | |
| } | |
| const response = await fetch( | |
| `https://api.github.com/search/issues?q=${encodeURIComponent(q)}&sort=created&order=${currentOrder}&per_page=${ROWS_PER_PAGE}&page=${currentPage + 1}`, | |
| { signal } | |
| ); | |
| if (!response.ok) { | |
| if (response.status === 403) { | |
| throw new Error("GitHub API rate limit exceeded."); | |
| } | |
| throw new Error("Failed to fetch data"); | |
| } | |
| const data = await response.json(); | |
| if (signal.aborted) return; | |
| setIssues(data.items); | |
| setTotalIssues(data.total_count > 1000 ? 1000 : data.total_count); // GitHub limits search results to 1000 | |
| } catch (err: unknown) { | |
| if (err instanceof DOMException && err.name === "AbortError") return; | |
| if (err instanceof Error) { | |
| setError(err.message || "Failed to fetch issues"); | |
| } else { | |
| setError("Failed to fetch issues"); | |
| } | |
| } finally { | |
| setLoading(false); | |
| } | |
| }, []); | |
| useEffect(() => { | |
| const controller = new AbortController(); | |
| fetchIssues(page, language, tag, sortOrder, controller.signal); | |
| return () => controller.abort(); | |
| }, [page, language, tag, sortOrder, fetchIssues]); |
🤖 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/pages/Issues/Issues.tsx` around lines 50 - 90, fetchIssues can race when
multiple requests are in-flight; create an AbortController ref (e.g.,
abortControllerRef) and before starting a new fetch in fetchIssues abort any
existing controller, then create a new controller and pass its signal to fetch;
in the catch block ignore abort errors (check err.name === 'AbortError' or err
instanceof DOMException) and only setError for real failures; also abort any
pending request in the useEffect cleanup (return () =>
abortControllerRef.current?.abort()) so stale responses never overwrite newer
state.
Related Issue
Description
This PR introduces a new Issues Tracking Page (
/issues) that allows users to explore open GitHub issues directly from the platform.Features Implemented
Integrated the GitHub Search API to dynamically fetch and display open issues
Added filtering options based on:
good first issue,bug, etc.)Implemented sorting functionality for:
Added
/issuesroute integration in both desktop and mobile Navbar componentsImproved code quality by:
SelectChangeEvent)How Has This Been Tested?
Screenshots
Type of Change
Summary by CodeRabbit
Release Notes