✨ feat(tracker): Improve Tracker page UI/UX & add Repositories tab#658
✨ feat(tracker): Improve Tracker page UI/UX & add Repositories tab#658Mayank251125 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. |
📝 WalkthroughWalkthroughAdds GitHub repository tracking: a typed ChangesRepository Tracking Feature
Sequence Diagram(s)sequenceDiagram
participant User
participant Tracker
participant useGitHubRepos
participant Octokit
User->>Tracker: submit(username, token?)
Tracker->>useGitHubRepos: fetchRepos(username, page, perPage, token?)
useGitHubRepos->>Octokit: GET /user/repos OR GET /users/{username}/repos
Octokit-->>useGitHubRepos: repos page, Link header
useGitHubRepos->>Octokit: (if page===1 && Link present) GET remaining pages with per_page=100
Octokit-->>useGitHubRepos: remaining repo pages
useGitHubRepos-->>Tracker: repos, allRepos, totalRepos (or error)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 @Mayank251125 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: 7
🤖 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/hooks/useGitHubRepos.ts`:
- Around line 57-68: The catch block in useGitHubRepos.ts swallowing errors
(inside fetchRepos) only calls setError and lets the promise resolve, so callers
like Tracker.tsx's handleSubmit (which uses toast.promise) incorrectly show
success; after each setError branch in the catch, propagate the failure by
re-throwing the original error (or return Promise.reject(err)) so fetchRepos
rejects and the caller can surface the correct toast/result—update the catch in
fetchRepos to throw err (or reject) after setError.
- Around line 49-56: The code incorrectly computes totalRepos by always
multiplying the rel="last" page number by perPage; change the logic in
useGitHubRepos to parse last page via lastMatch (keep linkHeader, lastMatch,
perPage, page, response.data.length, setTotalRepos) and compute total as
(lastPage - 1) * perPage + itemsOnLastPage, where itemsOnLastPage should be
response.data.length when the current page is the last page (lastPage === page)
and perPage otherwise; if you need exact totals even when the current page is
not the last, perform an extra fetch for the last page to read its length and
then setTotalRepos accordingly.
In `@src/pages/Tracker/Tracker.tsx`:
- Around line 615-626: The pagination effects currently read the mutable
`username` input, causing refetches to use unsaved edits; create and use a
committed `submittedUsername` state set by the search submit handler, then
change both effects (the tab/page effect that calls `fetchData` and the repoPage
effect that calls `fetchRepos`, and any other related effects around the 628-649
region) to read `submittedUsername` instead of `username` and include
`submittedUsername` in their dependency arrays; ensure the submit handler
assigns the current input to `submittedUsername` so all follow-up fetches
consistently use the last submitted username.
- Around line 428-442: The current filtering/sorting logic uses the paginated
array "repos" so search/filters (in the block producing "filtered") only run
against the current page; move the filtering/sorting to operate on the full
repository set (or fetch/cache all repos) before pagination is applied: ensure
the filter conditions referencing showForks, langFilter, search and the sort
logic using sort and sortDir are executed against the complete collection (e.g.,
"allRepos" or the fetched master list) and only then slice for the page, or
alternatively perform server-side filtering so the paginated "repos" is already
the filtered result.
- Around line 877-883: The tab elements rendered with Box currently only set
tabIndex on the active tab and handle Enter/Space but lack ArrowLeft/ArrowRight
roving focus; update the Tab keyboard handler to support left/right arrow
navigation by moving focus to the previous/next tab and calling setTab(newKey)
(wrapping at ends), and ensure tabIndex is 0 for the newly focused tab and -1
for others so focus moves correctly; modify the onKeyDown for the Box (and the
container with role="tablist" if present) to handle e.key ===
"ArrowRight"/"ArrowLeft" (and optionally Home/End) and also reset page/state via
setPage(0)/setStateFilter("all") when changing tabs to match existing click
behavior.
- Around line 444-455: The UI is showing page-only aggregates (totalStars,
totalForks computed from repos) but labels them as overall totals; update the
logic in Tracker.tsx so the summary uses the true overall totals or clearly
labels them as "this page": either (A) replace totalStars/totalForks with the
global totals source (if you have a backend/parent value like
totalStarsAll/totalForksAll or a paginatedTotals object) and use those in the
labels passed to fmtNum, or (B) change the label strings where
totalStars/totalForks (and the same occurrences around symbols at lines ~681-683
and ~855-863) are used to say "this page" (e.g., "stars on this page") so they
accurately reflect repos (the current page) rather than the entire dataset;
update every usage of totalStars and totalForks in Tracker.tsx accordingly.
🪄 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: 1f1699d0-178f-4270-8ff6-0ca376a08974
📒 Files selected for processing (2)
src/hooks/useGitHubRepos.tssrc/pages/Tracker/Tracker.tsx
- Fix 1: Switch to GET /user/repos for private repo support with PAT - Fix 2: Correct totalRepos count (fetch last page for exact number) - Fix 3: Re-throw errors so toast.promise shows error toast correctly - Fix 4: Fetch allRepos on load for global search/filter across all pages - Fix 5: Relabel stars/forks as page-only metrics - Fix 6: Add submittedUsername ref to prevent pagination using live input - Fix 7: Add ArrowLeft/ArrowRight roving focus to tablist for keyboard nav
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/hooks/useGitHubRepos.ts`:
- Around line 41-60: When token is present you currently always call 'GET
/user/repos' which returns repos for the token owner and ignores an arbitrary
username; change the logic in useGitHubRepos.ts to first, when token exists,
call the authenticated-user endpoint (e.g., octokit.rest.users.getAuthenticated
or GET /user) to obtain the token owner's login and compare it to the requested
username, and only use endpoint = 'GET /user/repos' (with visibility/affiliation
params) if the authenticated login equals username (or username is empty);
otherwise set endpoint = 'GET /users/{username}/repos' and include
params.username = username (and only include public/owner/type params
appropriate for that endpoint), then call octokit.request(endpoint, params) as
before.
🪄 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: 324087b5-b3a5-48d7-8b5e-d7c08a3efd9d
📒 Files selected for processing (2)
src/hooks/useGitHubRepos.tssrc/pages/Tracker/Tracker.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/pages/Tracker/Tracker.tsx
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/hooks/useGitHubRepos.ts (2)
50-52: ⚡ Quick winConsider caching the authenticated user to avoid repeated API calls.
GET /useris called on everyfetchReposinvocation when a token is present. For pagination scenarios, this results in redundant API calls (one per page change). Cache the authenticated login at hook level or on first token use.♻️ Proposed refactor
export const useGitHubRepos = (getOctokit: () => Octokit | null) => { const [repos, setRepos] = useState<GitHubRepo[]>([]); const [allRepos, setAllRepos] = useState<GitHubRepo[]>([]); const [totalRepos, setTotalRepos] = useState(0); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); + const [cachedAuthLogin, setCachedAuthLogin] = useState<string | null>(null); const fetchRepos = useCallback( async (username: string, page = 1, perPage = 12, token?: string) => { // ... if (token) { - const authUser = await octokit.request('GET /user'); - const authenticatedLogin = authUser.data.login.toLowerCase(); + let authenticatedLogin = cachedAuthLogin; + if (!authenticatedLogin) { + const authUser = await octokit.request('GET /user'); + authenticatedLogin = authUser.data.login.toLowerCase(); + setCachedAuthLogin(authenticatedLogin); + } // ... } }, - [getOctokit] + [getOctokit, cachedAuthLogin] );🤖 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/hooks/useGitHubRepos.ts` around lines 50 - 52, The hook currently calls octokit.request('GET /user') inside fetchRepos whenever token is present, causing redundant requests; modify the useGitHubRepos hook to cache the authenticated login (e.g., store a local variable or ref like cachedAuthenticatedLogin at hook scope) and set it on the first successful authUser fetch, then have fetchRepos reuse cachedAuthenticatedLogin instead of calling octokit.request again; ensure the cache is cleared or refreshed when token changes and keep the symbols authUser, authenticatedLogin, token, fetchRepos and octokit as the touchpoints for the change.
139-146: ⚖️ Poor tradeoffUnbounded concurrent requests could hit rate limits for users with many repositories.
Promise.allissues all remaining page requests simultaneously. A user with 500+ repositories triggers 5+ concurrent requests; extreme cases could exhaust the rate limit quickly or cause timeouts.Consider batching with a concurrency limit (e.g., 3-5 parallel requests) if this feature is expected to handle users with large repo counts.
🤖 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/hooks/useGitHubRepos.ts` around lines 139 - 146, The current Promise.all in useGitHubRepos.ts floods octokit.request for pages (see variables totalPages, endpoint, allParams and the const rest assignment), risking rate-limit/timeouts for large repo counts; change this to a bounded-concurrency fetcher that issues page requests in batches (e.g., concurrency 3-5) instead of all at once—either use a small utility like p-limit or implement a simple worker queue that schedules requests for octokit.request(endpoint, { ...allParams, page }) and collects results in order; ensure error handling and backoff/retry behavior is preserved when replacing the Promise.all call.
🤖 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/hooks/useGitHubRepos.ts`:
- Around line 103-113: The current logic computes a coarse "total" and
unconditionally calls setTotalRepos(total), overwriting a previously accurate
total (from all.length) when navigating to page > 1; change the update so that
setTotalRepos is only called when there is no existing accurate total or we're
on page 1 (i.e., preserve the current totalRepos state when page > 1 and
totalRepos is already populated). Locate the block using variables total,
lastPage, perPage, page and the calls setRepos(...) and setTotalRepos(...); add
a guard so setTotalRepos(total) is skipped when page > 1 and the stored
totalRepos is already set (or is greater than zero).
---
Nitpick comments:
In `@src/hooks/useGitHubRepos.ts`:
- Around line 50-52: The hook currently calls octokit.request('GET /user')
inside fetchRepos whenever token is present, causing redundant requests; modify
the useGitHubRepos hook to cache the authenticated login (e.g., store a local
variable or ref like cachedAuthenticatedLogin at hook scope) and set it on the
first successful authUser fetch, then have fetchRepos reuse
cachedAuthenticatedLogin instead of calling octokit.request again; ensure the
cache is cleared or refreshed when token changes and keep the symbols authUser,
authenticatedLogin, token, fetchRepos and octokit as the touchpoints for the
change.
- Around line 139-146: The current Promise.all in useGitHubRepos.ts floods
octokit.request for pages (see variables totalPages, endpoint, allParams and the
const rest assignment), risking rate-limit/timeouts for large repo counts;
change this to a bounded-concurrency fetcher that issues page requests in
batches (e.g., concurrency 3-5) instead of all at once—either use a small
utility like p-limit or implement a simple worker queue that schedules requests
for octokit.request(endpoint, { ...allParams, page }) and collects results in
order; ensure error handling and backoff/retry behavior is preserved when
replacing the Promise.all call.
🪄 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: 6d8b00b8-a3cc-4f24-8615-c4a9e05e3e64
📒 Files selected for processing (1)
src/hooks/useGitHubRepos.ts
| } else { | ||
| total = (lastPage - 1) * perPage + perPage; | ||
| } | ||
| } else { | ||
| total = (page - 1) * perPage + (response.data as GitHubRepo[]).length; | ||
| } | ||
|
|
||
| const pageRepos = response.data as GitHubRepo[]; | ||
|
|
||
| setRepos(pageRepos); | ||
| setTotalRepos(total); |
There was a problem hiding this comment.
totalRepos gets overwritten with inaccurate estimate on page > 1 fetches.
When page > 1 and a lastMatch exists, line 104 computes total = lastPage * perPage, which overestimates when the last page is partial. Line 113 unconditionally sets totalRepos to this estimate, overwriting the accurate value that was set via all.length during the initial page-1 fetch.
This causes pagination UI (context snippet at Tracker.tsx:562-578) to display incorrect page counts after navigating away from page 1.
🐛 Proposed fix: preserve totalRepos on page > 1 when already populated
+ // Only update totalRepos on page 1 or if not yet set
+ const shouldUpdateTotal = page === 1 || totalRepos === 0;
+
const pageRepos = response.data as GitHubRepo[];
setRepos(pageRepos);
- setTotalRepos(total);
+ if (shouldUpdateTotal) {
+ setTotalRepos(total);
+ }This preserves the accurate totalRepos (set from all.length on page 1) during subsequent pagination.
🤖 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/hooks/useGitHubRepos.ts` around lines 103 - 113, The current logic
computes a coarse "total" and unconditionally calls setTotalRepos(total),
overwriting a previously accurate total (from all.length) when navigating to
page > 1; change the update so that setTotalRepos is only called when there is
no existing accurate total or we're on page 1 (i.e., preserve the current
totalRepos state when page > 1 and totalRepos is already populated). Locate the
block using variables total, lastPage, perPage, page and the calls setRepos(...)
and setTotalRepos(...); add a guard so setTotalRepos(total) is skipped when page
> 1 and the stored totalRepos is already set (or is greater than zero).
Related Issue
Description
This PR significantly improves the Tracker page UI/UX, accessibility, and overall user
experience as proposed in the linked issue. It also introduces a brand new
Repositories Tab that fetches and displays real GitHub repository data.
🎨 UI/UX Improvements
🚀 UX Improvements
📊 Summary Stat Cards
🗂️ New: Repositories Tab
GET /users/{username}/reposusing Octokittopics, license, visibility badge, fork badge, last updated (relative time)
useGitHubRepos.ts🔍 Filtering & Data Experience
♿ Accessibility
aria-labelon all inputs, buttons, icons, and interactive elementsrole="tab"andaria-selectedon tab pillsrole="status"on stat cards and empty states:focus-visiblerings on every interactive element<span role="img">📱 Responsiveness
maxWidthbreakpoints per columnHow Has This Been Tested?
torvalds,Mayank251125)npm run devruns without errorsScreenshots (if applicable)
Type of Change
Summary by CodeRabbit
New Features
Bug Fixes / Reliability