feat: add issues tracking page with filters and sorting#273
feat: add issues tracking page with filters and sorting#273isauravanand wants to merge 3 commits into
Conversation
✅ Deploy Preview for github-spy ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds a new Issues page that queries GitHub Issues Search, provides language/label/sort filters with pagination, registers a ChangesIssues Page Feature
Sequence Diagram(s)sequenceDiagram
participant User
participant IssuesComponent
participant GitHubAPI
participant UITable
User->>IssuesComponent: selects filters or changes page
IssuesComponent->>GitHubAPI: fetch /search/issues?q={query}&page={n}
GitHubAPI-->>IssuesComponent: returns items + total_count / error
IssuesComponent->>UITable: update render (rows + pagination)
UITable-->>User: display results
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
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)
⚔️ Resolve merge conflicts
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.
|
@isauravanand : pls resolve conflicts |
# Conflicts: # src/components/Navbar.tsx
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
New Features
Refactor