Skip to content

feat: add top long-standing GSoC participants strip to organizations page#790

Open
anandhukrishnaas1 wants to merge 8 commits into
Sachinchaurasiya360:mainfrom
anandhukrishnaas1:feature/gssoc-featured-projects
Open

feat: add top long-standing GSoC participants strip to organizations page#790
anandhukrishnaas1 wants to merge 8 commits into
Sachinchaurasiya360:mainfrom
anandhukrishnaas1:feature/gssoc-featured-projects

Conversation

@anandhukrishnaas1
Copy link
Copy Markdown
Contributor

@anandhukrishnaas1 anandhukrishnaas1 commented May 29, 2026

Pull Request

Description

This pull request adds the "Most Active Organizations" section (labeled as "Long-standing participants") to the GSoC Organizations page (GSoCReposPage.tsx).

Key implementations:

  • Backend Endpoint: Created a GET /api/gsoc/top-orgs endpoint in the GSoC routes to find, sort, and slice the top 6 organizations by the number of years they have participated (yearsParticipated.length).
  • Query Key: Registered topOrgs() under the gsoc query key namespace.
  • Frontend Component: Implemented TopOrgsStrip, a horizontal scrollable container that renders cards containing the org logo/initials, organization name, and a custom rounded-md badge showing the years participated count. Clicking any card triggers the organization details modal.
  • Filter Behavior: The strip is automatically hidden if the user enters a search query or applies any filter (category, year, or tech stack).
  • Styling & Accessibility: Fully styled for both light and dark mode, using custom HSL colors and standard icons from lucide-react, matching the premium design system.

Related Issue

Fixes #714

Type of Change

  • Bug Fix
  • Feature
  • Enhancement
  • Documentation

Testing

  • Verified that the /api/gsoc/top-orgs endpoint correctly sorts and returns the top 6 organizations by participation length.
  • Verified that the TopOrgsStrip component retrieves data correctly using the React Query hook.
  • Tested that the component behaves correctly when filters are updated (shows on initial empty state/load, hides when search string is entered or a dropdown filter is selected).
  • Checked compatibility with light and dark mode styles to ensure readable contrast.

Screenshots

image

Checklist

  • Code follows project guidelines
  • No new compile/type errors
  • Tested manually (include steps above)
  • No .env, credentials, or node_modules committed
  • Docs updated (if needed)
  • Screenshots added for UI changes (if applicable)

Summary by CodeRabbit

  • New Features

    • Added a "Top long-standing participants" horizontal strip to the GSoC repositories page with smooth entrance animations and selectable orgs.
  • Backend

    • New endpoint to serve the top GSoC organizations and a data-seeding utility to populate organization records.
  • UI/UX Improvements

    • Refined project editor controls: adjusted edit/delete/tech buttons and add-technology styling for improved consistency.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

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 a backend /top-orgs endpoint and seed script, a React Query key and fetch, a horizontal TopOrgsStrip UI integrated into GSoCReposPage, small ProjectsSection button/UX tweaks, a refactor of useInterviewCountdown, and a CI step to generate the Prisma client.

Changes

Top Organizations Feature and Supporting Improvements

Layer / File(s) Summary
Backend /top-orgs endpoint
server/src/module/gsoc/gsoc.routes.ts
New Express GET /top-orgs route queries organizations, sorts by yearsParticipated length, truncates to 6, and returns them in { organizations }.
GSoC database seeding script
server/src/database/seed-gsoc.ts
New seed script fetching remote orgs, deriving slug, computing yearsParticipated and totalProjects, and upserting into prisma.gsocOrganization with mapped fields and error handling.
React Query key and fetch
client/src/lib/query-keys.ts, client/src/module/student/opensource/GSoCReposPage.tsx
Adds queryKeys.gsoc.topOrgs and a useQuery in GSoCReposPage to fetch /gsoc/top-orgs with infinite staleTime, exposing topOrgs.
TopOrgsStrip component and imports
client/src/module/student/opensource/GSoCReposPage.tsx
New TopOrgsStrip component: horizontal scroll container with left/right chevrons, framer-motion animated org items showing image/initial, name, and years; adds useRef and chevron icons.
GSoCReposPage strip integration
client/src/module/student/opensource/GSoCReposPage.tsx
Renders TopOrgsStrip when no filters are active and topOrgs exists; selecting an org finds it by slug and sets selectedOrg to drive the existing modal flow.
useInterviewCountdown changes
client/src/hooks/useInterviewCountdown.ts
Removes useCallback memoization for calculate, initializes state eagerly with useState(calculate()), and changes effect dependency to [targetDate] while preserving the interval update/clear behavior.
ProjectsSection UI updates
client/src/module/student/profile/components/ProjectsSection.tsx
Adjusts icon/button sizing and variants: edit/delete buttons to size="icon", delete uses variant="ghost", tech-remove size="icon", Add-technology uses variant="outline" and icon sizing.
CI Prisma client generation
.github/workflows/ci.yml
Adds npx prisma generate step in the test-server job before running unit tests to ensure Prisma client is generated.

Sequence Diagram

sequenceDiagram
  participant Client as Browser Client
  participant Page as GSoCReposPage
  participant Query as React Query
  participant API as /gsoc/top-orgs
  participant Strip as TopOrgsStrip
  participant Modal as Org Detail Modal

  Client->>Page: Load GSoCReposPage
  Page->>Query: useQuery("/gsoc/top-orgs")
  Query->>API: GET /gsoc/top-orgs
  API-->>Query: { organizations: [...top 6 orgs] }
  Query-->>Page: topOrgs array
  Page->>Strip: Render TopOrgsStrip (if !hasFilters && topOrgs)
  Client->>Strip: Click org card
  Strip->>Page: onSelect(orgSlug)
  Page->>Modal: Set selectedOrg and open modal
  Modal-->>Client: Show org details
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement, gssoc:approved

Suggested reviewers

  • Sachinchaurasiya360

"A rabbit hops across the strip so bright,
unveiling orgs that stood through night,
a click, a modal — history in sight,
students find mentors, hearts alight. 🐰✨"

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the primary change: adding a top long-standing GSoC participants strip to the organizations page, which matches the core functionality across all modified files.
Description check ✅ Passed The description comprehensively covers all required template sections including a detailed description of changes, related issue reference, type of change selection, thorough testing methodology, and a completed checklist.

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

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

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.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
client/src/module/student/opensource/GSoCReposPage.tsx (2)

126-141: ⚡ Quick win

Use the shared Button component for the scroll controls.

The new left/right chevron controls are raw <button> elements. Per the design system, new buttons should use the reusable Button (mode="icon", e.g. variant="ghost") from client/src/components/ui/button.tsx, which also keeps the focus/hover states consistent.

As per coding guidelines: "Use the reusable Button component from client/src/components/ui/button.tsx for all new buttons with variants (primary, secondary, mono, ghost, danger), modes (button, icon, link), and sizes (sm, md, lg)".

🤖 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 `@client/src/module/student/opensource/GSoCReposPage.tsx` around lines 126 -
141, Replace the raw <button> chevron controls in GSoCReposPage.tsx with the
shared Button component from client/src/components/ui/button.tsx: import Button
and swap the two onClick handlers that call scroll("left") and scroll("right")
into <Button mode="icon" variant="ghost" aria-label="Scroll left|right"
onClick={...}> containing the <ChevronLeft> / <ChevronRight> icons; ensure you
preserve existing className/size semantics by using the Button's props (e.g.,
size or className) so focus/hover states and accessibility are handled by the
shared Button component rather than raw elements.

102-108: 💤 Low value

Extract the top-org item type to avoid duplication.

The minimal org shape { id; name; slug; imageUrl?; yearsParticipated }[] is declared inline both here and at the useQuery call (line 599). Hoist a shared type (or reuse Pick<GSoCOrganization, ...>) so the strip props and the query stay in sync.

As per coding guidelines: "Apply DRY principle: no duplicate helpers, shared animation variants per file".

Also applies to: 599-604

🤖 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 `@client/src/module/student/opensource/GSoCReposPage.tsx` around lines 102 -
108, The org shape used inline in TopOrgsStrip props is duplicated elsewhere
(e.g., the useQuery result), so extract a shared type alias (e.g., TopOrg or
reuse Pick<GSoCOrganization, "id"|"name"|"slug"|"imageUrl"|"yearsParticipated">)
at the top of the file and replace the inline object type in the TopOrgsStrip
props and the useQuery return type with that alias to keep both in sync and
avoid duplication; update any references to TopOrgsStrip and the query (search
for TopOrgsStrip and the useQuery call around line ~599) to use the new shared
type.
server/src/module/gsoc/gsoc.routes.ts (1)

113-127: 💤 Low value

Consider a deterministic tiebreaker and bounded fetch.

findMany reads the full table into memory and sorts in JS. This mirrors the existing /stats handler so it's acceptable, but two small points:

  • Orgs with the same yearsParticipated.length come back in an arbitrary order, so the displayed top-6 can shift between requests. Add a secondary sort key (e.g. totalProjects or name) for stable results.
  • If the org table grows large, sorting by array length in Postgres (ORDER BY array_length("yearsParticipated", 1) DESC) via a raw query would avoid loading every row.
♻️ Deterministic tiebreaker
     const topOrgs = orgs
-      .sort((a, b) => b.yearsParticipated.length - a.yearsParticipated.length)
+      .sort(
+        (a, b) =>
+          b.yearsParticipated.length - a.yearsParticipated.length ||
+          a.name.localeCompare(b.name)
+      )
       .slice(0, 6);
🤖 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 `@server/src/module/gsoc/gsoc.routes.ts` around lines 113 - 127, The current
code uses prisma.gsocOrganization.findMany to load all orgs and then sorts in JS
to produce topOrgs by yearsParticipated.length, which yields non-deterministic
ties and loads the entire table; fix by making the tiebreaker deterministic in
the in-memory sort (update the comparator used for topOrgs to compare
b.yearsParticipated.length - a.yearsParticipated.length and, on equality,
compare a.name.localeCompare(b.name) or a.totalProjects if available) and, for
scalability, replace the fetch with a DB-side ordered, bounded query (use
prisma.$queryRaw to SELECT
id,name,slug,imageUrl,imageBgColor,category,yearsParticipated ORDER BY
array_length(yearsParticipated, 1) DESC, name ASC LIMIT 6) so you only fetch the
top 6 rows instead of the whole table.
🤖 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 `@client/src/module/student/opensource/GSoCReposPage.tsx`:
- Around line 730-738: The TopOrgsStrip click handler casts lightweight topOrgs
items into a full GSoCOrganization via setSelectedOrg(found as unknown as
GSoCOrganization), causing the modal to crash when the component reads missing
fields (detailOrg / detailData). Fix by mapping the found org into a safe
"partial filled" object in the click handler (in GSoCReposPage) that preserves
id/name/slug/imageUrl/yearsParticipated and adds safe defaults for all missing
fields the modal expects (e.g. technologies: [], topics: [], description: '',
links: [], mentors: [], any booleans/numbers set to sensible defaults) before
calling setSelectedOrg; keep the same type by constructing a
GSoCOrganization-shaped object rather than using as unknown as, so the modal can
render until the full detail query resolves.

---

Nitpick comments:
In `@client/src/module/student/opensource/GSoCReposPage.tsx`:
- Around line 126-141: Replace the raw <button> chevron controls in
GSoCReposPage.tsx with the shared Button component from
client/src/components/ui/button.tsx: import Button and swap the two onClick
handlers that call scroll("left") and scroll("right") into <Button mode="icon"
variant="ghost" aria-label="Scroll left|right" onClick={...}> containing the
<ChevronLeft> / <ChevronRight> icons; ensure you preserve existing
className/size semantics by using the Button's props (e.g., size or className)
so focus/hover states and accessibility are handled by the shared Button
component rather than raw elements.
- Around line 102-108: The org shape used inline in TopOrgsStrip props is
duplicated elsewhere (e.g., the useQuery result), so extract a shared type alias
(e.g., TopOrg or reuse Pick<GSoCOrganization,
"id"|"name"|"slug"|"imageUrl"|"yearsParticipated">) at the top of the file and
replace the inline object type in the TopOrgsStrip props and the useQuery return
type with that alias to keep both in sync and avoid duplication; update any
references to TopOrgsStrip and the query (search for TopOrgsStrip and the
useQuery call around line ~599) to use the new shared type.

In `@server/src/module/gsoc/gsoc.routes.ts`:
- Around line 113-127: The current code uses prisma.gsocOrganization.findMany to
load all orgs and then sorts in JS to produce topOrgs by
yearsParticipated.length, which yields non-deterministic ties and loads the
entire table; fix by making the tiebreaker deterministic in the in-memory sort
(update the comparator used for topOrgs to compare b.yearsParticipated.length -
a.yearsParticipated.length and, on equality, compare
a.name.localeCompare(b.name) or a.totalProjects if available) and, for
scalability, replace the fetch with a DB-side ordered, bounded query (use
prisma.$queryRaw to SELECT
id,name,slug,imageUrl,imageBgColor,category,yearsParticipated ORDER BY
array_length(yearsParticipated, 1) DESC, name ASC LIMIT 6) so you only fetch the
top 6 rows instead of the whole table.
🪄 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: fdeab902-0b04-4a9a-a4bb-f48601f249fc

📥 Commits

Reviewing files that changed from the base of the PR and between 6446d69 and b8472f8.

📒 Files selected for processing (5)
  • client/src/hooks/useInterviewCountdown.ts
  • client/src/lib/query-keys.ts
  • client/src/module/student/opensource/GSoCReposPage.tsx
  • client/src/module/student/profile/components/ProjectsSection.tsx
  • server/src/module/gsoc/gsoc.routes.ts

Comment thread client/src/module/student/opensource/GSoCReposPage.tsx
@anandhukrishnaas1
Copy link
Copy Markdown
Contributor Author

completed!

@Sachinchaurasiya360 Sachinchaurasiya360 added level:intermediate Requires moderate project understanding type:feature New feature implementation labels May 30, 2026
Copy link
Copy Markdown
Owner

@Sachinchaurasiya360 Sachinchaurasiya360 left a comment

Choose a reason for hiding this comment

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

Review: Changes Requested

The "Top long-standing participants" strip is a genuinely useful feature — surfacing orgs with multi-year participation helps students identify established communities. However the PR includes several unrelated changes that need to be addressed.

1. Unexplained ProjectsSection.tsx behavior change (required)

The diff removes mode="icon" from a Button in ProjectsSection.tsx. Per CLAUDE.md, mode: button is the default (not icon). Removing mode="icon" changes the button from an icon-only button to a text button, which would visually break the project editor UI. Please:

  • Confirm this is intentional and explain what visual change is expected, OR
  • Revert this change if it was accidental

2. useInterviewCountdown.ts changes are out of scope

The reformatting + adding clearInterval when the countdown hits zero are legitimate improvements (the missing clearInterval is a memory leak), but they don't belong in a PR titled "featured projects." Please either:

  • Open a separate PR for the countdown fix, OR
  • Add these changes to the PR description explicitly

3. PR title is too generic

"Feature/gssoc featured projects" should be more descriptive, e.g., "feat: add top long-standing GSoC participants strip to organizations page"

Please address item 1 (the behavior change in ProjectsSection) and update the description for items 2-3.

@anandhukrishnaas1
Copy link
Copy Markdown
Contributor Author

ok ,I will fix that

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
client/src/hooks/useInterviewCountdown.ts (1)

25-25: ⚡ Quick win

Use lazy initialization to avoid redundant computation on re-renders.

useState(calculate()) evaluates calculate() on every render, even though the result is only used during mount. Use the lazy initializer form to run the calculation once.

♻️ Suggested fix
-  const [timeLeft, setTimeLeft] = useState(calculate());
+  const [timeLeft, setTimeLeft] = useState(() => calculate());
🤖 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 `@client/src/hooks/useInterviewCountdown.ts` at line 25, The initial state for
timeLeft currently calls calculate() eagerly on every render; change the
useState invocation in useInterviewCountdown (the timeLeft state) to use the
lazy initializer form so calculate is executed only once on mount (i.e., replace
useState(calculate()) with useState(() => calculate()) to defer computation).
🤖 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.

Nitpick comments:
In `@client/src/hooks/useInterviewCountdown.ts`:
- Line 25: The initial state for timeLeft currently calls calculate() eagerly on
every render; change the useState invocation in useInterviewCountdown (the
timeLeft state) to use the lazy initializer form so calculate is executed only
once on mount (i.e., replace useState(calculate()) with useState(() =>
calculate()) to defer computation).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a74731b2-4495-4afd-9d26-a5e0b68a2728

📥 Commits

Reviewing files that changed from the base of the PR and between 70226b8 and 1c0b19f.

📒 Files selected for processing (2)
  • client/src/hooks/useInterviewCountdown.ts
  • client/src/module/student/profile/components/ProjectsSection.tsx

@anandhukrishnaas1 anandhukrishnaas1 changed the title Feature/gssoc featured projects feat: add top long-standing GSoC participants strip to organizations page May 30, 2026
@Sachinchaurasiya360
Copy link
Copy Markdown
Owner

Rebase it, Solve the conflict

…x seed-gsoc types, fix modal crash on TopOrgsStrip click

- CI (test-server): add missing 'prisma generate' step before tests
- vitest.config.ts: revert unnecessary alias hack (prisma generate is the proper fix)
- seed-gsoc.ts: fix Record<string,unknown> -> JSON.parse for Prisma InputJsonValue
- GSoCReposPage.tsx: seed all required GSoCOrganization fields when clicking
  a TopOrgsStrip card to prevent modal crash on .length access
@anandhukrishnaas1 anandhukrishnaas1 force-pushed the feature/gssoc-featured-projects branch from 072a21c to f5555fd Compare May 31, 2026 14:48
@github-actions github-actions Bot added quality:exceptional Exceptional implementation quality type:devops CI/CD, deployment, or infra changes level:advanced Complex implementation or logic and removed level:intermediate Requires moderate project understanding labels May 31, 2026
@github-actions github-actions Bot added level:critical High-impact or critical changes and removed level:advanced Complex implementation or logic labels May 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

level:critical High-impact or critical changes quality:exceptional Exceptional implementation quality type:devops CI/CD, deployment, or infra changes type:feature New feature implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add "Most Active Organizations" section on GSoCReposPage

2 participants