Skip to content

Feat/badges#195

Merged
PRODHOSH merged 19 commits into
PRODHOSH:mainfrom
abhilasha2101:feat/badges
Jun 25, 2026
Merged

Feat/badges#195
PRODHOSH merged 19 commits into
PRODHOSH:mainfrom
abhilasha2101:feat/badges

Conversation

@abhilasha2101

@abhilasha2101 abhilasha2101 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR implements a profile badges feature that allows users to showcase their participation in various open-source programs (like GSoC, Hacktoberfest, GSSoC, etc.) on their profile page. Profile owners can add new badges with specific years via an interactive dialog modal, and easily remove them if needed.

Related Issue

Closes #138

Type of Change

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

Changes Made

  • Database / Schema:
    • Created a migration file 20260623000000_add_badges_to_profiles.sql to add a badges jsonb column (defaulting to []) to the profiles table.
    • Updated supabase/schema.sql to keep the base schema structure in sync.
  • Frontend / Profile UI:
    • Added the BadgeItem interface to src/types/index.ts.
    • Updated ProfileView.tsx to render a list of badges with custom gradient styling corresponding to the program.
    • Implemented an add-badge modal dialog using the native <dialog> element allowing selection of the program (GSoC, MLH, Hacktoberfest, SWoC, GSSoC, EluSoC) and year (2020-2026).
    • Added logic to save and remove badges dynamically with proper loading indicators and permissions check (ensuring only the profile owner can modify badges).

AI Usage

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

Screenshots (if UI change)

Screenshot 2026-06-24 at 4 15 31 PM

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

Summary

  • New Features

    • Profile pages now show persisted program badge chips (program name with years) when available.
    • Profile owners can add or remove program badges using an owner-only modal dialog; updates reflect immediately.
  • Bug Fixes

    • Badge data now loads from saved profile settings, with a safe fallback if retrieval fails.
    • Improved badge chip color fallback for better visual consistency.
  • Style

    • Enhanced native dialog backdrop (semi-transparent dark with blur) for the badge modal experience.
    • Refined contribution heatmap layout and label rendering for cleaner display.

allcontributors Bot and others added 9 commits June 24, 2026 15:53
)

Adds year selector pills (current year minus 5) above the heatmap. Clicking a year fetches that year's contribution data via a new API route and re-renders the heatmap and streak counts.

Changes:
- New API route: src/app/api/[username]/contributions/route.ts
- New client component: src/components/profile/HeatmapWithYearNav.tsx
- Modified: src/lib/github.ts (added optional 'from' param to fetchContributionCalendar)
- Modified: src/components/profile/ProfileView.tsx (replaced inline heatmap with HeatmapWithYearNav)

Year pills use pill-tag-green for active and pill-tag-soft for inactive per DESIGN.md.

Signed-off-by: Adit Jain <aditjain2005@gmail.com>
Addresses CodeRabbit review:
- API route: validate year is 4-digit number in acceptable range, return 400 for bad input
- Client component: skip fetch when clicking the already-selected year

Signed-off-by: Adit Jain <aditjain2005@gmail.com>
Changed guard from (year === selectedYear) to (year === selectedYear && weeks.length > 0) so users can click the same year to retry after a network failure.

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

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds badge storage to public.profiles, threads badge data and profileId through the profile page, and adds owner-gated badge display, add/remove actions, and dialog styling in ProfileView.

Changes

Profile Badges Feature

Layer / File(s) Summary
Database schema and BadgeItem type
supabase/migrations/20260623000000_add_badges_to_profiles.sql, supabase/schema.sql, src/types/index.ts
Migration and schema add badges jsonb NOT NULL DEFAULT '[]'::jsonb to public.profiles; BadgeItem is exported with program and years fields.
Profile page fetch and prop wiring
src/app/[username]/page.tsx
Supabase query now selects id, score, updated_at, and badges; the result can populate badges and profileId, and ProfileView receives both props.
ProfileView badge state and persistence
src/components/profile/ProfileView.tsx
ProfileExtras adds badges and profileId; the component imports BadgeItem and Supabase, tracks auth and ownership state, syncs badge data and dialog state, and upserts badge changes to profiles.badges.
Badges section and add-badge dialog
src/components/profile/ProfileView.tsx, src/app/globals.css
Renders a conditional Badges section with styled chips and owner-only remove buttons, adds an owner-only add-badge dialog with program/year selectors, and styles the dialog backdrop blur.
ProfileView presentation adjustments
src/components/profile/ProfileView.tsx
Corrects the language chip fallback color and refactors the contribution heatmap container and month-label/week-grid rendering.

Sequence Diagram(s)

sequenceDiagram
  participant ProfileOwner
  participant ProfileView
  participant Supabase

  ProfileOwner->>ProfileView: open add-badge dialog
  ProfileView->>ProfileView: showModal()
  ProfileOwner->>ProfileView: choose program and year
  ProfileView->>Supabase: upsert profiles.badges
  Supabase-->>ProfileView: success or error
  ProfileView->>ProfileView: update badgesList

  ProfileOwner->>ProfileView: remove badge
  ProfileView->>Supabase: upsert filtered badges
  Supabase-->>ProfileView: success or error
  ProfileView->>ProfileView: update badgesList
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

  • PRODHOSH/ossfolio#185: Both PRs modify src/components/profile/ProfileView.tsx in the same UI area, including badge/pill rendering and related styling.

Suggested labels

completed

Suggested reviewers

  • PRODHOSH

Poem

🐇 A badge blooms on a profile page,
With years and colors on a cheerful stage.
Hop to save, then hop to see,
A chip for every memory.
The bunny grins, ears high and bright —
“Badges tucked in, looking right!”

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is related to the badge feature but too generic to clearly summarize the main change. Rename it to something specific like 'Add profile badges to OSSfolio profiles'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR implements profile badges, owner-only add/remove, Supabase storage, and the listed supported programs, matching #138.
Out of Scope Changes check ✅ Passed The other edits appear tied to the badge UI and related profile styling, with no clearly unrelated feature work.

✏️ 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: 3

🤖 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 112-130: The badges handling in the profile page still trusts
Array.isArray(profileRow.badges), which allows malformed entries through and can
break ProfileView when it reads BadgeItem fields. Update the badges parsing in
the page component to validate and filter each entry from profileRow.badges into
a real BadgeItem[] before assigning it, rejecting nulls and objects with invalid
program/years values so only well-formed badges are passed onward.

In `@src/components/profile/ProfileView.tsx`:
- Around line 1189-1205: The dialog in ProfileView.tsx needs an accessible name
so screen readers do not announce it as only “dialog”. Update the <dialog>
element used in the add-program-badge flow to reference the visible heading in
that same block (the h3 “Add Program Badge”) with aria-labelledby, or provide an
equivalent aria-label, so the dialog is properly labeled without changing its
visual behavior.
- Around line 142-149: Move the badge synchronization logic out of the render
path in ProfileView.tsx: the current `if (badges !== prevBadges)` block updates
state during render and can cause extra re-renders or loops when `badges` is a
new array reference. Remove `prevBadges` entirely and use an effect to sync
`badgesList` from `badges` in `ProfileView`, keeping the state update inside the
effect instead of directly in the component body.
🪄 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: ea8a0a22-d221-4aa9-a615-04e3a45cb16c

📥 Commits

Reviewing files that changed from the base of the PR and between 2b1dbf6 and 1e5e595.

📒 Files selected for processing (6)
  • src/app/[username]/page.tsx
  • src/app/globals.css
  • src/components/profile/ProfileView.tsx
  • src/types/index.ts
  • supabase/migrations/20260623000000_add_badges_to_profiles.sql
  • supabase/schema.sql

Comment thread src/app/[username]/page.tsx
Comment thread src/components/profile/ProfileView.tsx Outdated
Comment on lines +1189 to +1205
<dialog
ref={dialogRef}
style={{
border: "1px solid var(--color-hairline)",
borderRadius: "12px",
padding: "24px",
backgroundColor: "var(--color-canvas)",
color: "var(--color-ink)",
maxWidth: "400px",
width: "calc(100% - 32px)",
boxShadow: "0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)",
}}
>
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
<h3 style={{ fontSize: "18px", fontWeight: 600, margin: 0, color: "var(--color-ink)", letterSpacing: "-0.2px" }}>
Add Program Badge
</h3>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Give the dialog an accessible name.

Screen readers will currently announce this as just “dialog”. Link it to the heading with aria-labelledby or add an aria-label.

Suggested fix
         <dialog
           ref={dialogRef}
+          aria-labelledby="add-badge-title"
           style={{
...
-            <h3 style={{ fontSize: "18px", fontWeight: 600, margin: 0, color: "var(--color-ink)", letterSpacing: "-0.2px" }}>
+            <h3 id="add-badge-title" style={{ fontSize: "18px", fontWeight: 600, margin: 0, color: "var(--color-ink)", letterSpacing: "-0.2px" }}>
               Add Program Badge
             </h3>
📝 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.

Suggested change
<dialog
ref={dialogRef}
style={{
border: "1px solid var(--color-hairline)",
borderRadius: "12px",
padding: "24px",
backgroundColor: "var(--color-canvas)",
color: "var(--color-ink)",
maxWidth: "400px",
width: "calc(100% - 32px)",
boxShadow: "0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)",
}}
>
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
<h3 style={{ fontSize: "18px", fontWeight: 600, margin: 0, color: "var(--color-ink)", letterSpacing: "-0.2px" }}>
Add Program Badge
</h3>
<dialog
ref={dialogRef}
aria-labelledby="add-badge-title"
style={{
border: "1px solid var(--color-hairline)",
borderRadius: "12px",
padding: "24px",
backgroundColor: "var(--color-canvas)",
color: "var(--color-ink)",
maxWidth: "400px",
width: "calc(100% - 32px)",
boxShadow: "0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)",
}}
>
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
<h3 id="add-badge-title" style={{ fontSize: "18px", fontWeight: 600, margin: 0, color: "var(--color-ink)", letterSpacing: "-0.2px" }}>
Add Program Badge
</h3>
🧰 Tools
🪛 React Doctor (0.5.8)

[warning] 1189-1189: This dialog has no accessible name, so screen readers announce it as just “dialog.” Add aria-label or point aria-labelledby at its heading.

Give every <dialog> / role="dialog" an accessible name with aria-label or aria-labelledby (referencing the dialog's title element).

(dialog-has-accessible-name)

🤖 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` around lines 1189 - 1205, The dialog
in ProfileView.tsx needs an accessible name so screen readers do not announce it
as only “dialog”. Update the <dialog> element used in the add-program-badge flow
to reference the visible heading in that same block (the h3 “Add Program Badge”)
with aria-labelledby, or provide an equivalent aria-label, so the dialog is
properly labeled without changing its visual behavior.

Source: Linters/SAST tools

@PRODHOSH PRODHOSH added ELUSOC ELUSOC project submission in-progress VETERAN Advanced — 50 pts labels Jun 24, 2026
@PRODHOSH PRODHOSH self-requested a review June 24, 2026 13:05
@PRODHOSH

Copy link
Copy Markdown
Owner

@abhilasha2101 pls review and fix the coderabbit comments

@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: 2

Caution

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

⚠️ Outside diff range comments (2)
src/components/profile/ProfileView.tsx (2)

203-209: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Remove the auth debug log before shipping.

This logs internal user/profile identifiers in production browsers. It’s easy to drop now and avoids leaking auth metadata through client logs.

🤖 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` around lines 203 - 209, Remove the
auth debug logging from ProfileView before shipping, since it exposes internal
user/profile identifiers in the browser. Delete the console.log in ProfileView
that references profileId, authUser, user.login, and isOwner, and keep the
surrounding owner-check logic unchanged.

747-768: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add an accessible name to the remove button.

title is not a reliable accessible name, and the visible × is ambiguous. Add aria-label so screen-reader users know which badge will be removed.

Proposed fix
                         onClick={() => handleRemoveBadge(badge.program)}
                         title={`Remove ${badge.program} badge`}
+                        aria-label={`Remove ${badge.program} badge`}
🤖 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` around lines 747 - 768, The remove
badge button in ProfileView lacks a reliable accessible name because it only
uses the ambiguous “×” glyph and title. Update the button in the badge removal
UI to add an aria-label that clearly names the badge being removed, using the
existing badge.program value so screen readers announce the specific action.
Keep the button behavior and title as-is, but ensure the accessible name comes
from aria-label on the remove button element.
🤖 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 1038-1070: The heatmap month-label row in ProfileView is laid out
as a sibling of the week columns, so the labels render beside the chart instead
of above it. Update the markup around the heatmap block to use a vertical
wrapper for the labels and the week grid, keeping the month-label mapping logic
in the same ProfileView component but rendering the week columns in a separate
row beneath it.
- Line 972: The language indicator in ProfileView uses an invalid fallback color
for unknown languages. Update the fallback in the span style next to the
language label so the dot uses a valid CSS hex value, replacing the current
malformed fallback with the intended gray value; keep the change localized to
the LANG_COLORS lookup in ProfileView.

---

Outside diff comments:
In `@src/components/profile/ProfileView.tsx`:
- Around line 203-209: Remove the auth debug logging from ProfileView before
shipping, since it exposes internal user/profile identifiers in the browser.
Delete the console.log in ProfileView that references profileId, authUser,
user.login, and isOwner, and keep the surrounding owner-check logic unchanged.
- Around line 747-768: The remove badge button in ProfileView lacks a reliable
accessible name because it only uses the ambiguous “×” glyph and title. Update
the button in the badge removal UI to add an aria-label that clearly names the
badge being removed, using the existing badge.program value so screen readers
announce the specific action. Keep the button behavior and title as-is, but
ensure the accessible name comes from aria-label on the remove button element.
🪄 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: b0aa4441-23a0-49ff-a8d1-00a2f478202d

📥 Commits

Reviewing files that changed from the base of the PR and between 1e5e595 and 4a8a8bf.

📒 Files selected for processing (1)
  • src/components/profile/ProfileView.tsx

@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: 2

Caution

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

⚠️ Outside diff range comments (2)
src/components/profile/ProfileView.tsx (2)

203-209: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Remove the auth debug log before shipping.

This logs internal user/profile identifiers in production browsers. It’s easy to drop now and avoids leaking auth metadata through client logs.

🤖 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` around lines 203 - 209, Remove the
auth debug logging from ProfileView before shipping, since it exposes internal
user/profile identifiers in the browser. Delete the console.log in ProfileView
that references profileId, authUser, user.login, and isOwner, and keep the
surrounding owner-check logic unchanged.

747-768: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add an accessible name to the remove button.

title is not a reliable accessible name, and the visible × is ambiguous. Add aria-label so screen-reader users know which badge will be removed.

Proposed fix
                         onClick={() => handleRemoveBadge(badge.program)}
                         title={`Remove ${badge.program} badge`}
+                        aria-label={`Remove ${badge.program} badge`}
🤖 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` around lines 747 - 768, The remove
badge button in ProfileView lacks a reliable accessible name because it only
uses the ambiguous “×” glyph and title. Update the button in the badge removal
UI to add an aria-label that clearly names the badge being removed, using the
existing badge.program value so screen readers announce the specific action.
Keep the button behavior and title as-is, but ensure the accessible name comes
from aria-label on the remove button element.
🤖 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 1038-1070: The heatmap month-label row in ProfileView is laid out
as a sibling of the week columns, so the labels render beside the chart instead
of above it. Update the markup around the heatmap block to use a vertical
wrapper for the labels and the week grid, keeping the month-label mapping logic
in the same ProfileView component but rendering the week columns in a separate
row beneath it.
- Line 972: The language indicator in ProfileView uses an invalid fallback color
for unknown languages. Update the fallback in the span style next to the
language label so the dot uses a valid CSS hex value, replacing the current
malformed fallback with the intended gray value; keep the change localized to
the LANG_COLORS lookup in ProfileView.

---

Outside diff comments:
In `@src/components/profile/ProfileView.tsx`:
- Around line 203-209: Remove the auth debug logging from ProfileView before
shipping, since it exposes internal user/profile identifiers in the browser.
Delete the console.log in ProfileView that references profileId, authUser,
user.login, and isOwner, and keep the surrounding owner-check logic unchanged.
- Around line 747-768: The remove badge button in ProfileView lacks a reliable
accessible name because it only uses the ambiguous “×” glyph and title. Update
the button in the badge removal UI to add an aria-label that clearly names the
badge being removed, using the existing badge.program value so screen readers
announce the specific action. Keep the button behavior and title as-is, but
ensure the accessible name comes from aria-label on the remove button element.
🪄 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: b0aa4441-23a0-49ff-a8d1-00a2f478202d

📥 Commits

Reviewing files that changed from the base of the PR and between 1e5e595 and 4a8a8bf.

📒 Files selected for processing (1)
  • src/components/profile/ProfileView.tsx
🛑 Comments failed to post (2)
src/components/profile/ProfileView.tsx (2)

972-972: 🎯 Functional Correctness | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify hex color literals have valid CSS lengths: 3, 4, 6, or 8 digits.
python - <<'PY'
from pathlib import Path
import re

path = Path("src/components/profile/ProfileView.tsx")
for match in re.finditer(r"#[0-9A-Fa-f]+", path.read_text()):
    digits = len(match.group()) - 1
    if digits not in {3, 4, 6, 8}:
        print(f"{path}:{match.start()}: invalid hex literal {match.group()}")
PY

Repository: PRODHOSH/ossfolio

Length of output: 228


Use a valid fallback color here. #9a9aa is not a valid CSS hex value, so unknown languages can lose the dot color; use #9a9a9a instead.

🤖 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 972, The language indicator
in ProfileView uses an invalid fallback color for unknown languages. Update the
fallback in the span style next to the language label so the dot uses a valid
CSS hex value, replacing the current malformed fallback with the intended gray
value; keep the change localized to the LANG_COLORS lookup in ProfileView.

1038-1070: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fix the heatmap month-label layout.

The month-label row is currently the first flex child next to the week columns, so labels render beside the heatmap instead of above it. Wrap labels and weeks in a vertical container, then render the weeks in their own row.

🧰 Tools
🪛 React Doctor (0.5.8)

[warning] 1050-1050: Your users strain to read 11px text, so use at least 12px for body text, & 16px is best.

Use at least 12px for body text, and 16px is best. Small text is hard to read, especially on phones.

(no-tiny-text)

🤖 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` around lines 1038 - 1070, The heatmap
month-label row in ProfileView is laid out as a sibling of the week columns, so
the labels render beside the chart instead of above it. Update the markup around
the heatmap block to use a vertical wrapper for the labels and the week grid,
keeping the month-label mapping logic in the same ProfileView component but
rendering the week columns in a separate row beneath it.

@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

Caution

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

⚠️ Outside diff range comments (2)
src/components/profile/ProfileView.tsx (2)

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

Generate badge years from the current year.

The hard-coded 2026 default/options will stop allowing current-year badges after 2026.

Proposed fix
+  const currentYear = new Date().getFullYear();
   const [isBadgeModalOpen, setIsBadgeModalOpen] = useState(false);
   const [selectedProgram, setSelectedProgram] = useState("GSSoC");
-  const [selectedYear, setSelectedYear] = useState(2026);
+  const [selectedYear, setSelectedYear] = useState(() => new Date().getFullYear());
-                {[2026, 2025, 2024, 2023, 2022, 2021, 2020].map((y) => (
+                {Array.from({ length: currentYear - 2020 + 1 }, (_, i) => currentYear - i).map((y) => (
                   <option key={y} value={y}>{y}</option>
                 ))}

Also applies to: 1305-1307

🤖 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 135, The hard-coded year
value in ProfileView should be derived from the current year instead of fixed to
2026 so badge filtering stays valid in future years. Update the selectedYear
state initialization and any related year options logic in ProfileView
(including the badge-year generation around the referenced lines) to compute
years dynamically from the current date, keeping the existing behavior but
removing the static cutoff.

206-239: 🗄️ Data Integrity & Integration | 🟠 Major

Avoid inserting partial profile rows from badge edits.
profileId || authUser?.id lets both badge handlers upsert a new profiles row when none exists yet. That row is missing score/stats, so later profile reads can pick up the defaulted stored values instead of the live profile data. Gate badge edits on an existing profileId, or create the complete profile row upstream first.

🤖 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` around lines 206 - 239, Badge edits
in ProfileView are allowing a partial profiles upsert when no existing profile
row exists. Update the badge save flow in the relevant handler(s) to require an
existing profileId before calling supabase.from("profiles").upsert, or otherwise
ensure the full profile record is created upstream before badge updates. Use the
targetProfileId logic in ProfileView and the upsert call on profiles to locate
the fix, and keep badge updates from creating new rows with only
badges/updated_at/username.
🤖 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`:
- Line 1044: The month-label text in ProfileView is too small and should be
increased to improve readability. Update the inline style used for the heatmap
month labels in ProfileView so the fontSize is at least 12px, keeping the
existing layout and styling otherwise unchanged.

---

Outside diff comments:
In `@src/components/profile/ProfileView.tsx`:
- Line 135: The hard-coded year value in ProfileView should be derived from the
current year instead of fixed to 2026 so badge filtering stays valid in future
years. Update the selectedYear state initialization and any related year options
logic in ProfileView (including the badge-year generation around the referenced
lines) to compute years dynamically from the current date, keeping the existing
behavior but removing the static cutoff.
- Around line 206-239: Badge edits in ProfileView are allowing a partial
profiles upsert when no existing profile row exists. Update the badge save flow
in the relevant handler(s) to require an existing profileId before calling
supabase.from("profiles").upsert, or otherwise ensure the full profile record is
created upstream before badge updates. Use the targetProfileId logic in
ProfileView and the upsert call on profiles to locate the fix, and keep badge
updates from creating new rows with only badges/updated_at/username.
🪄 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: 34decffa-47e4-48e1-972b-44e20187523a

📥 Commits

Reviewing files that changed from the base of the PR and between 58c53e7 and e2ae11c.

📒 Files selected for processing (1)
  • src/components/profile/ProfileView.tsx

Comment thread src/components/profile/ProfileView.tsx Outdated

@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

Caution

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

⚠️ Outside diff range comments (2)
src/components/profile/ProfileView.tsx (2)

167-195: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Re-run dialog listener setup when the owner-only dialog mounts.

This effect runs before authUser resolves, when the {isOwner && <dialog>} is not mounted, so dialogRef.current is null and the close/backdrop listeners are never attached. Pressing Escape can close the native dialog while leaving isBadgeModalOpen stuck true.

Proposed fix
-  }, []);
+  }, [isOwner]);
🤖 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` around lines 167 - 195, The badge
modal dialog listeners in ProfileView are only attached once, so when the
owner-only dialog mounts after authUser resolves, dialogRef.current was
previously null and the close/click handlers never get registered. Update the
useEffect around dialogRef and setIsBadgeModalOpen so it reruns when the dialog
becomes available (and when the open state changes if needed), ensuring the
native dialog’s close/backdrop events always sync the modal state.

713-746: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Normalize JSONB badges before rendering.

src/app/[username]/page.tsx only checks Array.isArray(profileRow.badges) before passing JSON into ProfileView, but this render path assumes badge.years.join always exists. A malformed/old row can crash public profile rendering; sanitize to valid BadgeItem[] before storing in badgesList.

🤖 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` around lines 713 - 746, Normalize and
validate the badges payload before rendering in ProfileView so malformed JSONB
rows cannot crash the public profile page. Update the badges handling that feeds
badgesList and the render path in ProfileView to ensure each item is a valid
BadgeItem with a years array before using badge.years.join, and filter or coerce
invalid entries in the page.tsx data flow before passing them into the
component.
🤖 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 1312-1314: The participation year dropdown in ProfileView is
artificially capped to only the current year and previous six years, blocking
valid older entries. Update the year options generation in the dropdown map so
it supports older self-reported badge years instead of hardcoding a 7-year
range, while keeping the existing option rendering and value handling intact.

---

Outside diff comments:
In `@src/components/profile/ProfileView.tsx`:
- Around line 167-195: The badge modal dialog listeners in ProfileView are only
attached once, so when the owner-only dialog mounts after authUser resolves,
dialogRef.current was previously null and the close/click handlers never get
registered. Update the useEffect around dialogRef and setIsBadgeModalOpen so it
reruns when the dialog becomes available (and when the open state changes if
needed), ensuring the native dialog’s close/backdrop events always sync the
modal state.
- Around line 713-746: Normalize and validate the badges payload before
rendering in ProfileView so malformed JSONB rows cannot crash the public profile
page. Update the badges handling that feeds badgesList and the render path in
ProfileView to ensure each item is a valid BadgeItem with a years array before
using badge.years.join, and filter or coerce invalid entries in the page.tsx
data flow before passing them into the component.
🪄 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: dafd69eb-c001-44bc-903a-60bba2a5eac0

📥 Commits

Reviewing files that changed from the base of the PR and between e2ae11c and 2aec3f3.

📒 Files selected for processing (1)
  • src/components/profile/ProfileView.tsx

Comment thread src/components/profile/ProfileView.tsx Outdated
@PRODHOSH

Copy link
Copy Markdown
Owner

fix the merge conflicts @abhilasha2101

@abhilasha2101

Copy link
Copy Markdown
Contributor Author

@PRODHOSH I have resolved merge conflicts

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

Copy link
Copy Markdown

🎉 Your PR just got merged, @abhilasha2101 — 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

backend Supabase / API / database completed ELUSOC ELUSOC project submission frontend Related to UI / Next.js UI Visual / design changes VETERAN Advanced — 50 pts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] Add open source program participation badges to profiles (GSSoC, Hacktoberfest, EluSoC, etc.)

4 participants