Skip to content

feat: add org wishlist with heart toggle, localStorage persistence an…#1438

Open
Harkiratcodess wants to merge 3 commits into
Sachinchaurasiya360:mainfrom
Harkiratcodess:feat/gsoc-wishlist
Open

feat: add org wishlist with heart toggle, localStorage persistence an…#1438
Harkiratcodess wants to merge 3 commits into
Sachinchaurasiya360:mainfrom
Harkiratcodess:feat/gsoc-wishlist

Conversation

@Harkiratcodess
Copy link
Copy Markdown
Contributor

@Harkiratcodess Harkiratcodess commented Jun 5, 2026

closed #708

Summary

Adds a persistent wishlist feature to the GSoC Organizations page, allowing students to shortlist 3–5 orgs they plan to apply to without needing a separate Notion doc or text file.

Problem

Students browsing GSoC organizations had no way to mark favorites or track orgs they intended to apply to. They were forced to maintain external lists, creating friction in the application planning process.

Changes

  • Added a useWishlist custom hook that reads/writes org IDs to localStorage under the key gsoc_wishlist as a JSON number array
  • Added a Heart icon toggle button to each org card:
    • Filled lime when wishlisted, outline gray when not
    • Click stops propagation so it doesn't open the org modal
  • Added the same Heart toggle button in the org detail modal header
  • Added a "Wishlist (N)" filter chip in the filter bar:
    • Shows count of wishlisted orgs
    • When active, filters the grid to show only wishlisted orgs
  • Wishlist state survives page reload via localStorage persistence

Files Changed

  • client/src/module/student/opensource/GSoCReposPage.tsx
    • Added Heart to lucide-react imports
    • Added useWishlist custom hook
    • Updated GSoCOrgCard with wishlisted and onWishlistToggle props
    • Updated GSoCOrgModalProps interface and modal component
    • Added wishlist filter chip in filter bar
    • Wired useWishlist in GSoCReposPage with filteredOrganizations

Testing

  • Heart toggle renders on every org card and in the detail modal
  • Toggling persists to localStorage and survives page reload
  • Wishlist filter chip shows correct count and filters correctly
  • Clicking heart on card does not open the org modal

Summary by CodeRabbit

Release Notes

  • New Features
    • Added a wishlist feature for Google Summer of Code organizations that saves between sessions
    • Users can favorite organizations using a Heart button on organization cards and in the modal
    • Added a filter toggle to display only wishlisted organizations

@github-actions github-actions Bot added enhancement New feature or request good first issue Good for newcomers level:advanced Complex implementation or logic scope:frontend Changes to client-side / UI code type:feature New feature implementation quality:clean Clean and well-structured contribution labels Jun 5, 2026
@github-actions github-actions Bot added the gssoc:approved Approved for GSSoC scoring label Jun 5, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 5, 2026

Hi @Harkiratcodess, thanks for contributing to InternHack! 🎉

I have automatically:

  • 👤 Assigned this PR to you.
  • 🏷️ Applied the gssoc:approved label.

Our workflows will now analyze your changes to classify:

  • 📈 PR Difficulty: level:*
  • 🧩 PR Type: type:*
  • 🌟 PR Quality: quality:*

Tip

Ensure your PR description references the issue it resolves (e.g. Closes #123). This allows the bot to inherit any additional labels from that issue!

Happy coding! 🚀

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 5, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a persistent localStorage-backed wishlist feature to GSoCReposPage that allows users to mark favorite organizations via Heart buttons on cards and modals. Includes wishlist state management via a custom hook, filtering UI that shows only wishlisted organizations, and wiring throughout to pass wishlist state to components. Also includes non-semantic JSX restructuring for code clarity.

Changes

Organization Wishlist Feature

Layer / File(s) Summary
Wishlist Hook & Imports
client/src/module/student/opensource/GSoCReposPage.tsx
Adds Heart icon import and defines useWishlist custom hook that reads/writes wishlist IDs to localStorage under key gsoc_wishlist, with toggle(id), has(id), and raw wishlist array accessors.
Organization Card Wishlist UI
client/src/module/student/opensource/GSoCReposPage.tsx
Extends GSoCOrgCard signature to receive wishlisted boolean and onWishlistToggle handler. Renders Heart button with conditional fill styling and aria-label. Also reformats MetaChip and PlainChip type annotations and years display for clarity.
Modal Wishlist Integration
client/src/module/student/opensource/GSoCReposPage.tsx
Updates GSoCOrgModal props to accept wishlisted and onWishlistToggle. Adds Heart icon button to modal header with conditional styling. Refactors contact links section (Mailing List, ideas, guide) into explicit multi-line JSX structure.
Page-Level Wishlist State & Filtering
client/src/module/student/opensource/GSoCReposPage.tsx
Integrates useWishlist hook at page level, adds showWishlist toggle state, computes filteredOrganizations based on wishlist membership, and renders a Wishlist filter button displaying wishlist count.
Component Rendering & Wiring
client/src/module/student/opensource/GSoCReposPage.tsx
Maps filteredOrganizations to GSoCOrgCard components with wishlisted state and toggle handler (with propagation stoppage). Updates modal rendering to pass wishlisted state and callback enabling wishlist toggling from the detail modal.
UI Formatting & Restructuring
client/src/module/student/opensource/GSoCReposPage.tsx
Non-semantic JSX restructuring: empty-state text wrapping, organization description/chips, topics chips, year button labels, year card container markup, hero stats badge, and debounce formatting—all preserve rendered output while improving code clarity.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Sachinchaurasiya360/InternHack#846: Both PRs modify GSoCOrgCard component in GSoCReposPage.tsx to add per-organization UI features; potential merge conflict or review interaction point.

Suggested labels

enhancement, level:intermediate, quality:clean, type:feature, scope:frontend

Suggested reviewers

  • Sachinchaurasiya360

Poem

🐰 A wishlist for GSoC dreams, so grand,
Heart buttons flutter across each land,
LocalStorage keeps what you love so dear,
Filtering wishlists throughout the year,
Students now shortlist with crystal cheer! 💝

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

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.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly describes the main feature: adding org wishlist with heart toggle and localStorage persistence.
Description check ✅ Passed Description covers objectives, problem, changes, files modified, and testing; includes issue reference and UI screenshot note (though marked as 'No UI changes').
Linked Issues check ✅ Passed All requirements from issue #708 are met: Heart toggle on cards and modal, localStorage persistence with gsoc_wishlist key, wishlist filter chip with count, and state survives reload.
Out of Scope Changes check ✅ Passed Changes are limited to GSoCReposPage.tsx with focused additions of wishlist functionality; UI reformatting is minimal and directly supports the new feature.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/gsoc-wishlist

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

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

785-798: 💤 Low value

Consider using the shared Button component for consistency.

Per coding guidelines, new buttons should use the reusable Button component from client/src/components/ui/button.tsx. This applies to both the wishlist filter button here and the heart toggle in GSoCOrgCard.

🤖 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 785 -
798, Replace this inline button with the shared Button component to match the
app UI: use the Button from client/src/components/ui/button.tsx instead of the
raw <button>, wiring its onClick to setShowWishlist and rendering the Heart icon
and label based on showWishlist and wishlist.length; update className/variant
props on Button to reproduce the conditional styles now handled by showWishlist
(use variant/intent or className prop as available) and make the same change in
GSoCOrgCard for the heart toggle so both controls reuse the shared Button
component.

189-199: ⚡ Quick win

Wrap GSoCOrgCard with React.memo to optimize list re-renders.

GSoCOrgCard is rendered in a list and receives props that don't change frequently (org data, wishlisted boolean). Wrapping it with React.memo using the named function form would prevent unnecessary re-renders when parent state changes. Based on learnings: "Wrap list-rendered child components (cards, badges, list items) with React.memo when they receive stable props and don't have frequently changing internal state; use named function form."

Proposed fix
-function GSoCOrgCard({
+const GSoCOrgCard = React.memo(function GSoCOrgCard({
   org,
   onClick,
   wishlisted,
   onWishlistToggle,
 }: {
   org: GSoCOrganization;
   onClick: () => void;
   wishlisted: boolean;
   onWishlistToggle: (e: React.MouseEvent) => void;
 }) {
   // ... component body
-}
+});

Also add React to the import or use memo directly:

-import { useState, type ReactNode } from "react";
+import { useState, memo, type ReactNode } from "react";

Also applies to: 835-852

🤖 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 189 -
199, Wrap the GSoCOrgCard named function with React.memo to prevent unnecessary
re-renders when rendered in lists: modify the export/definition so the component
is memoized (e.g., export default React.memo(GSoCOrgCard) or const
MemoGSoCOrgCard = React.memo(GSoCOrgCard) and use that in the parent), and
ensure React is imported (or import memo directly) so memo is available; keep
the function signature and props unchanged to preserve identity checks for
memoization.
🤖 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 244-260: The nested interactive element issue: the wishlist toggle
button (onWishlistToggle / Heart / wishlisted span) is rendered inside the
card's clickable <button> (card click handler), which is invalid; refactor the
card wrapper (the element that currently is a clickable button in GSoCReposPage)
into a non-button container with role="button", tabIndex={0}, and keyboard
handlers (onKeyDown to handle Enter/Space) so the inner <button> can remain
interactive, or alternatively move the wishlist <button> (using
onWishlistToggle, Heart, wishlisted) outside the card button area and ensure
only one interactive control handles card activation; update any click handlers
that depended on the card <button> to use the new container's role/button
behavior.
- Around line 33-40: The initializer for useWishlist reads JSON from
localStorage but doesn't validate the parsed value; JSON.parse may return null
or a non-array which will make later calls like wishlist.includes() throw. In
the useWishlist initializer (the useState hook that sets wishlist and
setWishlist using WISHLIST_KEY), after JSON.parse check that the result is an
array (Array.isArray) and that its items are numbers (e.g., filter/validate
entries), otherwise return an empty array; ensure the state always receives a
number[] fallback.

---

Nitpick comments:
In `@client/src/module/student/opensource/GSoCReposPage.tsx`:
- Around line 785-798: Replace this inline button with the shared Button
component to match the app UI: use the Button from
client/src/components/ui/button.tsx instead of the raw <button>, wiring its
onClick to setShowWishlist and rendering the Heart icon and label based on
showWishlist and wishlist.length; update className/variant props on Button to
reproduce the conditional styles now handled by showWishlist (use variant/intent
or className prop as available) and make the same change in GSoCOrgCard for the
heart toggle so both controls reuse the shared Button component.
- Around line 189-199: Wrap the GSoCOrgCard named function with React.memo to
prevent unnecessary re-renders when rendered in lists: modify the
export/definition so the component is memoized (e.g., export default
React.memo(GSoCOrgCard) or const MemoGSoCOrgCard = React.memo(GSoCOrgCard) and
use that in the parent), and ensure React is imported (or import memo directly)
so memo is available; keep the function signature and props unchanged to
preserve identity checks for memoization.
🪄 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: 9618fb80-2ec1-44ea-862c-deab71b2f012

📥 Commits

Reviewing files that changed from the base of the PR and between 0c176ae and 0bd369f.

📒 Files selected for processing (1)
  • client/src/module/student/opensource/GSoCReposPage.tsx

Comment thread client/src/module/student/opensource/GSoCReposPage.tsx
Comment thread client/src/module/student/opensource/GSoCReposPage.tsx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request good first issue Good for newcomers gssoc:approved Approved for GSSoC scoring level:advanced Complex implementation or logic quality:clean Clean and well-structured contribution scope:frontend Changes to client-side / UI code type:feature New feature implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add GSoC org "add to wishlist" feature

1 participant