Skip to content

feat(repos): show topic badges on repo cards#201

Merged
PRODHOSH merged 10 commits into
PRODHOSH:mainfrom
Adit-Jain-srm:feat/150-repo-topic-badges
Jun 25, 2026
Merged

feat(repos): show topic badges on repo cards#201
PRODHOSH merged 10 commits into
PRODHOSH:mainfrom
Adit-Jain-srm:feat/150-repo-topic-badges

Conversation

@Adit-Jain-srm

@Adit-Jain-srm Adit-Jain-srm commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds repository topic badges to the repo cards on profile pages. Topics give instant context about what a project does (e.g., "react", "machine-learning", "cli-tool").

Related Issue

Closes #150

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Refactor
  • Chore / dependency update

Changes Made

  1. Fetch topics - Changed the Accept header in fetchGitHubRepos to application/vnd.github.mercy-preview+json which includes the topics array in the REST response
  2. Type updates - Added topics?: string[] to GitHubRepo interface in ProfileView and GitHubRepoLike in profile-data.ts, plus topics: string[] to the Repo type
  3. Map topics - mapRepos now passes topics through (defaults to [])
  4. Render pills - Up to 3 topic pills shown below the repo description, with "+N" overflow indicator if more exist

Pill style matches DESIGN.md's pill-tag-soft pattern: canvas-soft background, hairline border, rounded-full, micro (11px) font size.

AI Usage

  • I did not use AI for any part of the code in this PR
  • I used AI for coding (Cursor with Claude) and I fully understand every change I made, including which functions I changed, why I changed them, and what side effects they could create

The mercy-preview Accept header is GitHub's way of opting into the topics field in REST responses. The topics come as a flat string[] on each repo object. The pill rendering uses inline styles matching the existing pattern in the codebase.

Screenshots (if UI change)

Topic pills appear as small grey rounded badges below each repo description, before the language/stars/forks row. Example: react typescript nextjs +2

Checklist

  • I was assigned to the issue before opening this PR
  • My branch is up to date with main
  • Code works locally and I have tested it
  • No console.log left in src/
  • If schema changed - both schema.sql and a new migration file are included
  • If this is a UI change - I read DESIGN.md and followed the design system (colors, spacing, typography, components)
  • Docs updated if needed
  • PR title follows Conventional Commits format (feat:, fix:, docs:, etc.)
  • This PR description is written in my own words

Summary by CodeRabbit

  • New Features
    • Repository cards now show GitHub topic tags as pill elements, including a “+N more” pill when there are more than three topics.
  • Bug Fixes
    • Improved GitHub profile fetching and profile page behavior when GitHub user data isn’t available.
    • Enhanced profile metadata generation so titles/descriptions reliably fall back when GitHub responses can’t be used, and use updated GitHub response formatting.

…loses PRODHOSH#175)

fetchGitHubUser now returns a discriminated union instead of nullable JSON. The page component only calls notFound() on a genuine 404. Rate limits and server errors throw, which the error boundary catches with a retry option. generateMetadata falls back gracefully on any failure.

Signed-off-by: Adit Jain <aditjain2005@gmail.com>
… assertion

Addresses CodeRabbit review:
- fetchGitHubUser: wrapped in try-catch so network errors return { status: 'error' } instead of throwing
- fetchGitHubRepos: wrapped in try-catch for consistency with other fetch functions
- Replaced (user.name as string) with typeof runtime check
- Skipped generateMetadata try-catch: redundant since fetchGitHubUser now never throws

Signed-off-by: Adit Jain <aditjain2005@gmail.com>
…ags)

Merges the discriminated union error handling from this PR with the OG/Twitter metadata from PR PRODHOSH#174 (now on main). Uses typeof runtime checks to safely access user fields from Record<string, unknown>.

Signed-off-by: Adit Jain <aditjain2005@gmail.com>
Signed-off-by: Adit Jain <aditjain2005@gmail.com>
Replaced Record<string, unknown> with a proper interface matching the GitHub REST API response. Removes redundant typeof guards in generateMetadata. Fixes tsc --noEmit failure where ProfileView expected typed GitHubUser.

Signed-off-by: Adit Jain <aditjain2005@gmail.com>
Adds repository topics as pill badges on each repo card (up to 3 + overflow count).

Changes:
- Accept header changed to mercy-preview to include topics in REST response
- GitHubRepo and GitHubRepoLike interfaces updated with topics field
- mapRepos passes topics through
- ProfileView renders topic pills using pill-tag-soft style from DESIGN.md

Signed-off-by: Adit Jain <aditjain2005@gmail.com>
@github-actions github-actions Bot added frontend Related to UI / Next.js backend Supabase / API / database UI Visual / design changes labels Jun 24, 2026
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds repository topics to the repo data flow and renders them as pills on repo cards. It also changes GitHub user fetching to use a different repos request media type and status-based metadata generation.

Changes

Profile topics and user fetch handling

Layer / File(s) Summary
topics field: contract, mapping, and UI
src/types/index.ts, src/lib/profile-data.ts, src/lib/github.ts, src/components/profile/ProfileView.tsx
Repo, GitHubRepoLike, and local repo types gain topics fields; mapped repos receive topics arrays; repo cards render up to 3 topic pills with a +N more pill.
user fetch header and metadata
src/app/[username]/page.tsx
fetchGitHubRepos changes its GitHub request header, and generateMetadata returns fallback metadata or builds profile metadata from successful user data.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • PRODHOSH/ossfolio#128: Both PRs modify src/components/profile/ProfileView.tsx’s “Popular repositories” UI with conditional per-repo rendering.
  • PRODHOSH/ossfolio#174: Both PRs modify src/app/[username]/page.tsx’s generateMetadata to derive metadata from fetched GitHub user data.
  • PRODHOSH/ossfolio#199: Both PRs modify src/app/[username]/page.tsx metadata behavior around fetched GitHub user results.

Suggested labels

ADVENTURER

Suggested reviewers

  • PRODHOSH

Poem

🐇 Hop-hop, the topics bloom in a row,
Three little pills with a +N glow.
ok and fallback now take their cue,
And metadata sings in a fresher hue.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning src/app/[username]/page.tsx also changes generateMetadata behavior, which is unrelated to showing repo topic badges. Remove the metadata refactor or split it into a separate PR unless it is required for the topic-badge feature.
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly states the main change: showing topic badges on repo cards.
Linked Issues check ✅ Passed The PR fetches topics, adds the repo topics type, and renders up to 3 badges with overflow, matching #150.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/components/profile/ProfileView.tsx`:
- Around line 547-548: The overflow badge in ProfileView should use the required
“+N more” label instead of only “+N”. Update the JSX in ProfileView.tsx where
the topics overflow count is rendered so the displayed text includes the word
“more” while still using repo.topics.length and the existing overflow
calculation logic.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 48eff74f-d57a-4c81-ba9e-83f8c8ff07c1

📥 Commits

Reviewing files that changed from the base of the PR and between 85f65a5 and 829971d.

📒 Files selected for processing (4)
  • src/app/[username]/page.tsx
  • src/components/profile/ProfileView.tsx
  • src/lib/profile-data.ts
  • src/types/index.ts

Comment thread src/components/profile/ProfileView.tsx Outdated
…flow label

Signed-off-by: Adit Jain <aditjain2005@gmail.com>
@Adit-Jain-srm

Copy link
Copy Markdown
Contributor Author

Re the pre-merge warnings:

  1. Topic fetch IS included - line 62 of page.tsx changes the Accept header to mercy-preview which enables topics in the REST response. The check may have missed it since it's a one-line header swap.
  2. Out of scope - the only change in page.tsx is the Accept header (one line). The other functions in that file are pre-existing on main.
  3. Docstring coverage - informational, same as all other PRs in this repo.

@PRODHOSH PRODHOSH self-requested a review June 25, 2026 04:41
@PRODHOSH PRODHOSH added ELUSOC ELUSOC project submission in-progress ADVENTURER Intermediate — 25 pts fix-merge-conflict labels Jun 25, 2026
Took main's updated page.tsx and reapplied the mercy-preview Accept header for topic fetching. All other topic changes (types, profile-data, ProfileView, github.ts) merged cleanly.

Signed-off-by: Adit Jain <aditjain2005@gmail.com>
Signed-off-by: Adit Jain <aditjain2005@gmail.com>
@Adit-Jain-srm

Copy link
Copy Markdown
Contributor Author

Merge conflicts resolved. Ready for review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/app/[username]/page.tsx (2)

56-72: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

generateMetadata reads a result shape fetchGitHubUser no longer returns.

fetchGitHubUser now returns either null or the raw user object (res.json()), but this code treats it as a discriminated union: result.status !== "ok" (Line 57) and result.data (Line 63). result.status will always be undefined, so the OK branch always runs, and when result is null (non-OK/error) result.status throws a TypeError. result.data is also always undefined, so the user fields below dereference undefined.

Branch on null and use the result directly as the user.

🔧 Proposed fix
-  const result = await fetchGitHubUser(username);
-  if (result.status !== "ok") {
+  const user = await fetchGitHubUser(username);
+  if (!user) {
     return {
       title: `${username} - OSSfolio`,
       description: `Open-source profile for ${username}.`,
     };
   }
-  const user = result.data;
   const displayName = user.name || username;
🤖 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/app/`[username]/page.tsx around lines 56 - 72, `generateMetadata` in the
page component is still treating `fetchGitHubUser` as a status/data union even
though it now returns either `null` or the raw user object. Update the logic to
branch on a null result from `fetchGitHubUser(username)` and use the returned
object directly as the user, removing the `status` and `data` access in
`generateMetadata` so the metadata fields are built from the actual user
payload.

116-123: 🩺 Stability & Availability | 🔴 Critical

Replace the stale userResult branch in src/app/[username]/page.tsx:116-123. fetchGitHubUser now returns null on non-OK responses and only throws RateLimit, so userResult.status/code/data is invalid here; const user = userResult.data also redeclares user and breaks the build.

🤖 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/app/`[username]/page.tsx around lines 116 - 123, The `userResult`
handling in `Page` is stale and no longer matches `fetchGitHubUser`’s return
shape. Update the branch around `userResult` to handle the new contract: treat
`null` as the not-found case, let the `RateLimit` throw path propagate, and
remove any `status/code/data` access. Also fix the duplicate `user` declaration
by assigning the fetched value to the existing `user` binding instead of
redeclaring it.

Source: Linters/SAST tools

🤖 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/app/`[username]/page.tsx:
- Around line 97-102: The fetched page state in the [username]/page.tsx
component is using `any`, which removes type safety for the rest of the page.
Update the declarations for user, repos, liveStats, orgs, and
contributionCalendar to use the concrete return types from the corresponding
fetch helpers, and thread those types through the logic that consumes them in
the page component.
- Around line 28-38: The rate-limit path in the data fetch logic is swallowing
the intended exception inside the JSON parsing try/catch, so the `RateLimit`
signal never reaches `ProfilePage`. Update the fetch helper in
`src/app/[username]/page.tsx` to inspect the failed GitHub response first, store
whether it matches a rate-limit condition, then throw the `RateLimit` error
after the parsing block so it escapes the empty catch; this allows the `catch
(e)` handling in `ProfilePage` to set `rateLimited` correctly.

In `@src/components/profile/ProfileView.tsx`:
- Line 697: The language swatch in ProfileView uses an invalid fallback hex
color, so the inline style fallback is ignored. Update the fallback in the span
rendering for the language badge to use the valid 6-digit gray color already
used elsewhere in ProfileView (the repo-language swatch), keeping the
LANG_COLORS lookup and the badge markup otherwise unchanged.

---

Outside diff comments:
In `@src/app/`[username]/page.tsx:
- Around line 56-72: `generateMetadata` in the page component is still treating
`fetchGitHubUser` as a status/data union even though it now returns either
`null` or the raw user object. Update the logic to branch on a null result from
`fetchGitHubUser(username)` and use the returned object directly as the user,
removing the `status` and `data` access in `generateMetadata` so the metadata
fields are built from the actual user payload.
- Around line 116-123: The `userResult` handling in `Page` is stale and no
longer matches `fetchGitHubUser`’s return shape. Update the branch around
`userResult` to handle the new contract: treat `null` as the not-found case, let
the `RateLimit` throw path propagate, and remove any `status/code/data` access.
Also fix the duplicate `user` declaration by assigning the fetched value to the
existing `user` binding instead of redeclaring it.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 289eb550-e8e8-49c9-9f30-f222c85b4dde

📥 Commits

Reviewing files that changed from the base of the PR and between 3b1d4d5 and 7dbaa05.

📒 Files selected for processing (4)
  • src/app/[username]/page.tsx
  • src/components/profile/ProfileView.tsx
  • src/lib/github.ts
  • src/lib/profile-data.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/app/[username]/page.tsx (2)

56-72: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

generateMetadata reads a result shape fetchGitHubUser no longer returns.

fetchGitHubUser now returns either null or the raw user object (res.json()), but this code treats it as a discriminated union: result.status !== "ok" (Line 57) and result.data (Line 63). result.status will always be undefined, so the OK branch always runs, and when result is null (non-OK/error) result.status throws a TypeError. result.data is also always undefined, so the user fields below dereference undefined.

Branch on null and use the result directly as the user.

🔧 Proposed fix
-  const result = await fetchGitHubUser(username);
-  if (result.status !== "ok") {
+  const user = await fetchGitHubUser(username);
+  if (!user) {
     return {
       title: `${username} - OSSfolio`,
       description: `Open-source profile for ${username}.`,
     };
   }
-  const user = result.data;
   const displayName = user.name || username;
🤖 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/app/`[username]/page.tsx around lines 56 - 72, `generateMetadata` in the
page component is still treating `fetchGitHubUser` as a status/data union even
though it now returns either `null` or the raw user object. Update the logic to
branch on a null result from `fetchGitHubUser(username)` and use the returned
object directly as the user, removing the `status` and `data` access in
`generateMetadata` so the metadata fields are built from the actual user
payload.

116-123: 🩺 Stability & Availability | 🔴 Critical

Replace the stale userResult branch in src/app/[username]/page.tsx:116-123. fetchGitHubUser now returns null on non-OK responses and only throws RateLimit, so userResult.status/code/data is invalid here; const user = userResult.data also redeclares user and breaks the build.

🤖 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/app/`[username]/page.tsx around lines 116 - 123, The `userResult`
handling in `Page` is stale and no longer matches `fetchGitHubUser`’s return
shape. Update the branch around `userResult` to handle the new contract: treat
`null` as the not-found case, let the `RateLimit` throw path propagate, and
remove any `status/code/data` access. Also fix the duplicate `user` declaration
by assigning the fetched value to the existing `user` binding instead of
redeclaring it.

Source: Linters/SAST tools

🤖 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/app/`[username]/page.tsx:
- Around line 97-102: The fetched page state in the [username]/page.tsx
component is using `any`, which removes type safety for the rest of the page.
Update the declarations for user, repos, liveStats, orgs, and
contributionCalendar to use the concrete return types from the corresponding
fetch helpers, and thread those types through the logic that consumes them in
the page component.
- Around line 28-38: The rate-limit path in the data fetch logic is swallowing
the intended exception inside the JSON parsing try/catch, so the `RateLimit`
signal never reaches `ProfilePage`. Update the fetch helper in
`src/app/[username]/page.tsx` to inspect the failed GitHub response first, store
whether it matches a rate-limit condition, then throw the `RateLimit` error
after the parsing block so it escapes the empty catch; this allows the `catch
(e)` handling in `ProfilePage` to set `rateLimited` correctly.

In `@src/components/profile/ProfileView.tsx`:
- Line 697: The language swatch in ProfileView uses an invalid fallback hex
color, so the inline style fallback is ignored. Update the fallback in the span
rendering for the language badge to use the valid 6-digit gray color already
used elsewhere in ProfileView (the repo-language swatch), keeping the
LANG_COLORS lookup and the badge markup otherwise unchanged.

---

Outside diff comments:
In `@src/app/`[username]/page.tsx:
- Around line 56-72: `generateMetadata` in the page component is still treating
`fetchGitHubUser` as a status/data union even though it now returns either
`null` or the raw user object. Update the logic to branch on a null result from
`fetchGitHubUser(username)` and use the returned object directly as the user,
removing the `status` and `data` access in `generateMetadata` so the metadata
fields are built from the actual user payload.
- Around line 116-123: The `userResult` handling in `Page` is stale and no
longer matches `fetchGitHubUser`’s return shape. Update the branch around
`userResult` to handle the new contract: treat `null` as the not-found case, let
the `RateLimit` throw path propagate, and remove any `status/code/data` access.
Also fix the duplicate `user` declaration by assigning the fetched value to the
existing `user` binding instead of redeclaring it.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 289eb550-e8e8-49c9-9f30-f222c85b4dde

📥 Commits

Reviewing files that changed from the base of the PR and between 3b1d4d5 and 7dbaa05.

📒 Files selected for processing (4)
  • src/app/[username]/page.tsx
  • src/components/profile/ProfileView.tsx
  • src/lib/github.ts
  • src/lib/profile-data.ts
🛑 Comments failed to post (3)
src/app/[username]/page.tsx (2)

28-38: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

RateLimit error is swallowed and never propagates.

The throw new Error("RateLimit") at Line 33 sits inside the try whose catch {} (Line 35) is empty, so the throw is immediately caught and discarded; the function then falls through to return null. As a result, the catch (e) rate-limit detection in ProfilePage (Line 107) can never fire and rateLimited stays false.

Detect the rate limit first, then throw outside the parsing try.

🔧 Proposed fix
   if (!res.ok) {
-    // Detect rate limit based on GitHub response
-    try {
-      const err = await res.json();
-      if (err.message && err.message.toLowerCase().includes("rate limit")) {
-        throw new Error("RateLimit");
-      }
-    } catch {}
-    return null;
+    // Detect rate limit based on GitHub response
+    let isRateLimit = false;
+    try {
+      const err = await res.json();
+      isRateLimit = !!err.message && err.message.toLowerCase().includes("rate limit");
+    } catch {}
+    if (isRateLimit) throw new Error("RateLimit");
+    return null;
   }
📝 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.

  if (!res.ok) {
    // Detect rate limit based on GitHub response
    let isRateLimit = false;
    try {
      const err = await res.json();
      isRateLimit = !!err.message && err.message.toLowerCase().includes("rate limit");
    } catch {}
    if (isRateLimit) throw new Error("RateLimit");
    return null;
  }
  return res.json();
🤖 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/app/`[username]/page.tsx around lines 28 - 38, The rate-limit path in the
data fetch logic is swallowing the intended exception inside the JSON parsing
try/catch, so the `RateLimit` signal never reaches `ProfilePage`. Update the
fetch helper in `src/app/[username]/page.tsx` to inspect the failed GitHub
response first, store whether it matches a rate-limit condition, then throw the
`RateLimit` error after the parsing block so it escapes the empty catch; this
allows the `catch (e)` handling in `ProfilePage` to set `rateLimited` correctly.

97-102: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Avoid any for the fetched values.

user, repos, liveStats, orgs, and contributionCalendar are all typed any, which disables type checking across the rest of the page (and contributed to the undetected userResult/shape mismatch above). Prefer the concrete return types from the fetch helpers.

🧰 Tools
🪛 GitHub Actions: CI - Build & Lint / 0_build.txt

[error] 98-98: TypeScript (tsc --noEmit) error TS2451: Cannot redeclare block-scoped variable 'user'.

🪛 GitHub Actions: CI - Build & Lint / build

[error] 98-98: TypeScript (tsc --noEmit) failed: TS2451 Cannot redeclare block-scoped variable 'user'.

🪛 GitHub Check: build

[failure] 98-98:
Cannot redeclare block-scoped variable 'user'.

🤖 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/app/`[username]/page.tsx around lines 97 - 102, The fetched page state in
the [username]/page.tsx component is using `any`, which removes type safety for
the rest of the page. Update the declarations for user, repos, liveStats, orgs,
and contributionCalendar to use the concrete return types from the corresponding
fetch helpers, and thread those types through the logic that consumes them in
the page component.
src/components/profile/ProfileView.tsx (1)

697-697: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Invalid fallback hex color #9a9aa.

#9a9aa has only 5 digits and is not a valid CSS hex color, so the fallback swatch will be ignored/transparent. The repo-language swatch on Line 550 correctly uses #9a9a9a.

🎨 Proposed fix
-                <span style={{ width: "10px", height: "10px", backgroundColor: LANG_COLORS[language] ?? "`#9a9aa`", borderRadius: "9999px", flexShrink: 0, display: "inline-block" }}></span>{language}
+                <span style={{ width: "10px", height: "10px", backgroundColor: LANG_COLORS[language] ?? "`#9a9a9a`", borderRadius: "9999px", flexShrink: 0, display: "inline-block" }}></span>{language}
📝 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.

                <span style={{ width: "10px", height: "10px", backgroundColor: LANG_COLORS[language] ?? "`#9a9a9a`", borderRadius: "9999px", flexShrink: 0, display: "inline-block" }}></span>{language}
🤖 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/components/profile/ProfileView.tsx` at line 697, The language swatch in
ProfileView uses an invalid fallback hex color, so the inline style fallback is
ignored. Update the fallback in the span rendering for the language badge to use
the valid 6-digit gray color already used elsewhere in ProfileView (the
repo-language swatch), keeping the LANG_COLORS lookup and the badge markup
otherwise unchanged.

Signed-off-by: Adit Jain <aditjain2005@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/[username]/page.tsx (1)

56-63: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Restore the actual fetchGitHubUser return contract here.

Lines 57-63 assume fetchGitHubUser() returns { status, data }, but this file still uses it as user | null at Line 116. That mismatch breaks metadata generation in both directions: successful lookups always take the fallback path because result.status is undefined, and failed lookups can crash on null.status before returning fallback metadata. Read the returned user object directly here, or change fetchGitHubUser to a consistent discriminated union everywhere.

Suggested fix
 export async function generateMetadata({ params }: ProfilePageProps): Promise<Metadata> {
   const { username } = await params;
-  const result = await fetchGitHubUser(username);
-  if (result.status !== "ok") {
+  const user = await fetchGitHubUser(username);
+  if (!user) {
     return {
       title: `${username} - OSSfolio`,
       description: `Open-source profile for ${username}.`,
     };
   }
-  const user = result.data;
   const displayName = user.name || username;
   const bio = user.bio || "";
🤖 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/app/`[username]/page.tsx around lines 56 - 63, The metadata loader in
page.tsx is using the wrong `fetchGitHubUser` contract, causing it to treat
successful responses as failures and risking a null dereference on failed
responses. Update the logic around `fetchGitHubUser`, `generateMetadata`, and
the `user` handling so they all use one consistent return shape: either read the
user object directly here and handle null safely, or convert `fetchGitHubUser`
to a discriminated union and update every caller to match.
🤖 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.

Outside diff comments:
In `@src/app/`[username]/page.tsx:
- Around line 56-63: The metadata loader in page.tsx is using the wrong
`fetchGitHubUser` contract, causing it to treat successful responses as failures
and risking a null dereference on failed responses. Update the logic around
`fetchGitHubUser`, `generateMetadata`, and the `user` handling so they all use
one consistent return shape: either read the user object directly here and
handle null safely, or convert `fetchGitHubUser` to a discriminated union and
update every caller to match.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2e72a350-0f53-4511-9935-32815482d4f0

📥 Commits

Reviewing files that changed from the base of the PR and between 7dbaa05 and 1304f59.

📒 Files selected for processing (1)
  • src/app/[username]/page.tsx

@PRODHOSH PRODHOSH merged commit 6db83b4 into PRODHOSH:main Jun 25, 2026
6 checks passed
@github-actions

Copy link
Copy Markdown

🎉 Your PR just got merged, @Adit-Jain-srm — thank you for contributing to OSSfolio!

Your work is now part of the project. Here's what to do next:

  • ⭐ If you haven't already, consider giving the repo a star — it helps us grow.
  • 📢 Share your contribution on LinkedIn, Twitter, or wherever you hang out. You shipped open source!
  • 🔍 Browse other open issues if you want to keep contributing.

We really appreciate you taking the time. See you in the next PR! 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ADVENTURER Intermediate — 25 pts backend Supabase / API / database completed ELUSOC ELUSOC project submission frontend Related to UI / Next.js UI Visual / design changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] Show repository topics as tag badges on repo cards

2 participants