Skip to content

Add full-stack API-backed wishlist feature#6

Draft
Copilot wants to merge 3 commits into
mainfrom
copilot/add-wishlist-feature-api
Draft

Add full-stack API-backed wishlist feature#6
Copilot wants to merge 3 commits into
mainfrom
copilot/add-wishlist-feature-api

Conversation

Copy link
Copy Markdown

Copilot AI commented May 6, 2026

Adds a complete wishlist feature: in-memory REST API with Swagger docs, React Query-powered context, heart button on product cards, dedicated wishlist page, and nav link with count badge.

Backend (api/)

  • models/wishlist.tsWishlistItem interface (userId: string, productId: number, addedAt: string) with Swagger schema annotation
  • routes/wishlist.ts — In-memory store; three endpoints with full Swagger JSDoc:
    • GET /api/wishlist?userId= — 400 if userId missing
    • POST /api/wishlist — 409 on duplicate
    • DELETE /api/wishlist/:productId?userId= — 404 if not found
    • Exports resetWishlist() for test isolation
  • routes/wishlist.test.ts — 8 Vitest/Supertest tests covering happy paths and all error cases
  • index.ts — Mounts router at /api/wishlist

Frontend (frontend/)

  • AuthContext — Adds userEmail: string | null; set on login(), cleared on logout()
  • WishlistContext — Fetches wishlist via React Query (enabled when logged in); exposes wishlistProductIds (memoized Set<number>), toggleWishlist (useCallback, calls POST or DELETE), isWishlisted, wishlistCount
  • Products.tsx — Heart button in image top-right; filled green (text-primary fill-primary) when wishlisted, outline gray otherwise; aria-label reflects add/remove state; stops click propagation
  • Wishlist.tsx — Filters all products to wishlisted ones; empty state: "Your wishlist is empty — browse products to save items for later"; same card layout with heart button
  • App.tsx — Wraps tree in <WishlistProvider>; adds <Route path="/wishlist">
  • Navigation.tsx — Wishlist link visible only when isLoggedIn; count badge when wishlistCount > 0
  • api/config.ts — Adds wishlist: '/api/wishlist' endpoint constant
Original prompt

Wishlist Feature — Variation 2: Full-stack API-backed wishlist

Add a wishlist feature with a proper backend API. User identity is derived from the email stored in AuthContext (no passwords sent — just email as the user identifier).

Backend changes (api/)

1. Model (api/src/models/wishlist.ts)

export interface WishlistItem {
  userId: string;       // user's email
  productId: number;
  addedAt: string;      // ISO date string
}

Include Swagger JSDoc @swagger components.schemas.WishlistItem matching this shape.

2. Route (api/src/routes/wishlist.ts)

In-memory store pattern (same as product.ts). Register under /api/wishlist.

Endpoints:

  • GET /api/wishlist?userId={email} — returns WishlistItem[] for that user (400 if missing userId)
  • POST /api/wishlist — body { userId, productId }, adds item, returns 201 with the new WishlistItem (409 if already exists)
  • DELETE /api/wishlist/:productId?userId={email} — removes item, returns 204 (404 if not found)

Include Swagger JSDoc annotations on every endpoint following the exact style used in api/src/routes/product.ts.

3. Register in api/src/index.ts

Import the wishlist router and add:

app.use('/api/wishlist', wishlistRoutes);

Frontend changes (frontend/)

4. Extend AuthContext (frontend/src/context/AuthContext.tsx)

  • Add userEmail: string | null to the context type and state
  • Set it during login() from the email parameter
  • Clear it on logout()

5. WishlistContext (frontend/src/context/WishlistContext.tsx)

  • On mount (when isLoggedIn and userEmail are set), fetch the user's wishlist via GET /api/wishlist?userId={email} using React Query (useQuery)
  • Expose: wishlistProductIds: Set<number>, toggleWishlist(productId), isWishlisted(productId), wishlistCount: number
  • toggleWishlist calls POST or DELETE via axios based on current state, then invalidates/refetches the query
  • When isLoggedIn is false, wishlist is empty

6. Heart button on product cards (frontend/src/components/entity/product/Products.tsx)

  • Add heart icon button in the top-right corner of each product card's image area
  • Filled green heart (text-primary) when wishlisted, outline gray heart when not
  • Clicking toggles: toggleWishlist(product.productId), stop propagation
  • Accessible: aria-label mentioning add/remove and product name

7. Wishlist page (frontend/src/components/wishlist/Wishlist.tsx)

  • Uses wishlistProductIds from context to know which products to show
  • Fetches all products via useQuery, filters to wishlisted ones
  • Empty state: "Your wishlist is empty — browse products to save items for later"
  • Same product card layout with heart button; uses useTheme() for dark mode

8. Route and navigation

  • frontend/src/App.tsx: Add <Route path="/wishlist" element={<Wishlist />} />
  • frontend/src/components/Navigation.tsx: Add "Wishlist" nav link (visible only when isLoggedIn), with a count badge when wishlistCount > 0

Constraints

  • Follow existing code patterns in the repo (arrow function components, Tailwind CSS, JSDoc Swagger style from product.ts)
  • API endpoints must have full Swagger annotations
  • No database — use in-memory arrays exactly like existing routes

Copilot AI and others added 2 commits May 6, 2026 14:50
Copilot AI changed the title [WIP] Add backend API for wishlist feature Add full-stack API-backed wishlist feature May 6, 2026
Copilot AI requested a review from thomasiverson May 6, 2026 14:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants