Switch leaderboard to real API data and show contributor points#36
Switch leaderboard to real API data and show contributor points#36jinhojang6 wants to merge 21 commits intodevelopfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…leaderboard pages
d50b050 to
cd76a7d
Compare
… in contributor item
…ributor-related components and translations
There was a problem hiding this comment.
Pull request overview
This work-in-progress PR implements a seasonal leaderboard feature alongside the existing all-time (historical) leaderboard. The implementation migrates from Hasura-based APIs to a new contribute API endpoint, removes mock data generation, and introduces real-time data fetching with sorting capabilities.
Changes:
- Migrated from Hasura API to new contribute API for stats and contributor data
- Added seasonal leaderboard with current season tracking alongside all-time rankings
- Introduced sort dropdown UI component with "points" and "newest" options
- Updated contributor data model to include points and tier information
Reviewed changes
Copilot reviewed 20 out of 21 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| types/index.d.ts | Added optional points and tier fields to Contributor interface |
| messages/*.json | Changed label from "score" to "point" across all languages |
| lib/utils.ts | Temporarily hardcoded API URL for testing (needs reversion) |
| lib/leaderboard-utils.ts | Removed mock data generation functions |
| lib/jsonld-schemas.ts | Removed leaderboard-specific JSON-LD schema generation |
| hooks/useSocialProofData.ts | Refactored to use new contribute API endpoint |
| hooks/useSeasonalLeaderboard.ts | New hook for fetching seasonal leaderboard data |
| hooks/useContributors.ts | Updated to support sorting, limits, and new API structure |
| components/ui/sort-dropdown.tsx | New dropdown component for sorting options |
| components/leaderboard/leaderboard-tabs.tsx | Swapped tab order (historical first, seasonal second) |
| components/leaderboard/leaderboard-table.tsx | Removed in favor of ContributorDirectory component |
| components/contributors/*.tsx | Updated to show points, optional tiers, and handle missing data |
| containers/leaderboard/leaderboard-container.tsx | Major refactor to support tabbed interface with sorting |
| containers/leaderboard/tier-system-container.tsx | Increased top margin spacing |
| containers/leaderboard/scoring-system-container.tsx | Minor class order change |
| containers/directory/directory-container.tsx | Removed (functionality merged into leaderboard) |
| app/[locale]/leaderboard/page.tsx | Updated to use LeaderboardContainer instead of DirectoryContainer |
Comments suppressed due to low confidence (1)
components/contributors/contributor-item.tsx:71
- Duplicate link with empty href: similar to the username link, when profileUrl is empty, this button will link to nowhere. Consider conditionally rendering this button only when the contributor has a valid GitHub profile URL.
<a href={contributor.profileUrl} target="_blank" rel="noopener noreferrer">
<Button variant="outlined" size="small">
{t('contributors.viewGithubProfile')}
</Button>
</a>
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const data = (await res.json()) as ContributorApiResponse[] | ||
|
|
||
| return data.map((contributor, idx) => ({ | ||
| id: idx + 1, | ||
| username: contributor.login, | ||
| profileUrl: contributor.profileUrl, | ||
| contributions: contributor.contributionCount, | ||
| latestContribution: contributor.latest.date, | ||
| latestRepo: contributor.latest.repo, | ||
| avatarUrl: `https://github.com/${contributor.login}.png`, | ||
| })) | ||
| return data.map((contributor) => { | ||
| const points = toNumber(contributor.total_points) | ||
|
|
||
| return { | ||
| id: toNumber(contributor.contributor_id), | ||
| username: contributor.github_username || contributor.alias, | ||
| profileUrl: contributor.github_username | ||
| ? `https://github.com/${contributor.github_username}` | ||
| : '', | ||
| points, | ||
| contributions: toNumber(contributor.contribution_count), | ||
| tier: getTierName(contributor.rank_name, points), | ||
| latestContribution: contributor.latest_contribution_at || '', | ||
| latestRepo: contributor.latest_repo || '', | ||
| avatarUrl: contributor.github_username | ||
| ? `https://github.com/${contributor.github_username}.png` | ||
| : '', | ||
| } | ||
| }) |
There was a problem hiding this comment.
Missing validation for API response structure. If the backend returns an unexpected structure (e.g., data is null or not an array), this will throw an unhandled error at runtime. Consider adding a guard check: if (!data || !Array.isArray(data)) before mapping the data.
| <img | ||
| src={contributor.avatarUrl} | ||
| alt={`${contributor.username} avatar`} | ||
| className="h-10 w-10 rounded-full sm:h-12 sm:w-12" |
There was a problem hiding this comment.
Potential issue with empty avatarUrl: when a contributor has no GitHub username, avatarUrl is set to an empty string. This will cause a broken image to be displayed. Consider adding a fallback placeholder image or conditionally rendering the image only when avatarUrl is available.
| const json = (await res.json()) as SeasonApiResponse | ||
|
|
||
| const contributors = json.data.map((entry) => ({ | ||
| id: toNumber(entry.contributor_id), | ||
| username: entry.github_username || entry.alias, | ||
| profileUrl: entry.github_username ? `https://github.com/${entry.github_username}` : '', | ||
| points: toNumber(entry.season_points), | ||
| contributions: toNumber(entry.contribution_count), | ||
| latestContribution: entry.latest_contribution_at || '', | ||
| latestRepo: entry.latest_repo || '', | ||
| avatarUrl: entry.github_username ? `https://github.com/${entry.github_username}.png` : '', | ||
| })) |
There was a problem hiding this comment.
Missing validation for API response structure. If the backend returns an unexpected structure (e.g., json.data is null or undefined), this will throw an unhandled error at runtime. Consider adding a guard check: if (!json.data || !Array.isArray(json.data)) before mapping the data.
…y username or repo
…der and ContributorDetailsContainer
…isplaying contributor history with pagination
…ContributorDirectory for table view
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
/leaderboardto the newLeaderboardContainerand remove old dev/legacy leaderboard components.points,newest).SortDropdownand fix dropdown visibility in dark mode.total_points,contribution_count,latest_*, rank info) with safe number parsing./contribute/stats.