-
Notifications
You must be signed in to change notification settings - Fork 205
✨ feat(tracker): Improve Tracker page UI/UX & add Repositories tab #658
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| import { useState, useCallback } from 'react'; | ||
| import { Octokit } from '@octokit/core'; | ||
|
|
||
| export interface GitHubRepo { | ||
| id: number; | ||
| name: string; | ||
| full_name: string; | ||
| html_url: string; | ||
| description: string | null; | ||
| language: string | null; | ||
| stargazers_count: number; | ||
| forks_count: number; | ||
| open_issues_count: number; | ||
| visibility: string; | ||
| fork: boolean; | ||
| pushed_at: string; | ||
| created_at: string; | ||
| updated_at: string; | ||
| topics: string[]; | ||
| license: { name: string } | null; | ||
| default_branch: string; | ||
| size: number; | ||
| } | ||
|
|
||
| export const useGitHubRepos = (getOctokit: () => Octokit | null) => { | ||
| const [repos, setRepos] = useState<GitHubRepo[]>([]); | ||
| const [totalRepos, setTotalRepos] = useState(0); | ||
| const [loading, setLoading] = useState(false); | ||
| const [error, setError] = useState(''); | ||
|
|
||
| const fetchRepos = useCallback( | ||
| async (username: string, page = 1, perPage = 12) => { | ||
| const octokit = getOctokit(); | ||
| if (!octokit || !username.trim()) return; | ||
|
|
||
| setLoading(true); | ||
| setError(''); | ||
|
|
||
| try { | ||
| const response = await octokit.request('GET /users/{username}/repos', { | ||
| username, | ||
| per_page: perPage, | ||
| page, | ||
| sort: 'pushed', | ||
| direction: 'desc', | ||
| type: 'owner', | ||
| }); | ||
|
|
||
| const linkHeader = (response.headers as any)?.link ?? ''; | ||
| const lastMatch = linkHeader.match(/page=(\d+)>; rel="last"/); | ||
| const total = lastMatch | ||
| ? parseInt(lastMatch[1], 10) * perPage | ||
| : (page - 1) * perPage + response.data.length; | ||
|
|
||
| setRepos(response.data as GitHubRepo[]); | ||
| setTotalRepos(total); | ||
|
Comment on lines
+49
to
+56
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Multiplying 🧰 Tools🪛 ESLint[error] 49-49: Unexpected any. Specify a different type. ( 🤖 Prompt for AI Agents |
||
| } catch (err: any) { | ||
| const status = err?.status; | ||
| const message = err?.message?.toLowerCase() ?? ''; | ||
|
|
||
| if (status === 403) { | ||
| setError('GitHub API rate limit exceeded. Please provide a PAT to continue.'); | ||
| } else if (status === 404 || message.includes('not found')) { | ||
| setError('User not found. Please check the GitHub username.'); | ||
| } else if (status === 401) { | ||
| setError('Invalid token. Please check your Personal Access Token.'); | ||
| } else { | ||
| setError('Unable to fetch repositories. Please verify the username or network connection.'); | ||
|
Comment on lines
+57
to
+68
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Propagate repo fetch failures back to the caller. This catch path only updates local state, so 🧰 Tools🪛 ESLint[error] 57-57: Unexpected any. Specify a different type. ( 🤖 Prompt for AI Agents |
||
| } | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| }, | ||
| [getOctokit] | ||
| ); | ||
|
|
||
| return { repos, totalRepos, loading, error, fetchRepos }; | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: GitMetricsLab/github_tracker
Length of output: 9576
🏁 Script executed:
Repository: GitMetricsLab/github_tracker
Length of output: 37926
🌐 Web query:
GitHub REST API GET /users/{username}/repos private repositories authenticated visibility=all include private💡 Result:
GitHub REST API does not support the exact endpoint/parameters in your query. 1) Endpoint mismatch - GET /users/{username}/repos is the endpoint for listing repositories for a specified user, but the REST API docs describe a limited set of query parameters (not a visibility=all style “include private” for that endpoint) and it’s not the authenticated-user listing endpoint. [1] 2) Correct endpoint for “include private” - To list both public and private repositories, you should use the “list repositories for the authenticated user” endpoint: GET /user/repos. [1] - That endpoint supports a visibility query parameter with allowed values all, public, private; default is all. [1] - Therefore, to include private repos, use visibility=private (or visibility=all) together with authentication. [1] 3) Authentication/scope requirement - Private repositories are returned only if the authenticated token/app has sufficient OAuth scope/permissions; generally GitHub requires appropriate repo/user permissions for private data. A related users endpoint notes that OAuth tokens need the repo scope to include private information. [2] Practical example (REST): - GET https://api.github.com/user/repos?visibility=all - Authorization: Bearer (must be authorized to access the private repos) [1][2] If you tell me whether you’re using a Personal Access Token, GitHub App installation token, or OAuth app token (and which scopes/permissions you have), I can recommend the exact visibility/affiliation/type filters and the right auth headers.
Citations:
Fix repo fetching: private-repo endpoint, pagination count, and toast success/error contract
useGitHubReposrequestsGET /users/{username}/repos(withtype: 'owner'), which won’t include private repositories even with a PAT; switch to the authenticated-user endpoint (GET /user/repos) and passvisibility=all|private(with proper token scopes).totalReposmultiplies therel="last"page number byperPage, which overestimates when the last page is partial.fetchReposswallows errors (setserrorstate but never rethrows), soTracker.tsx’stoast.promise(Promise.all([p1, p2]))can show the success message even when repos fail to load; makefetchReposreject on failure (or change toast to usereposError).