This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
SWAP is a Next.js 14 social network and marketplace for trading collectible stickers (World Cup 2026 Panini album). Users manage their collections, follow other collectors, chat, join communities, and coordinate trades through a marketplace.
npm run dev # start dev server (http://localhost:3000)
npm run build # production build
npm run start # start production server
npm run lint # ESLint (next/core-web-vitals)No test runner is configured. No API routes — all backend calls go directly to Supabase.
Required in .env.local:
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=
Stack: Next.js 14 App Router · TypeScript · Tailwind CSS · Supabase (Postgres + Auth + Storage + Realtime)
Route groups:
app/(auth)/— public auth pages (login, register, forgot/reset password)app/(protected)/— authenticated pages with shared sidebar + bottom nav layoutcolecciones/— sticker collection viewermarketplace/— profile-based trade matching (sorted by sticker compatibility)chat/[tradeId]/— per-trade and direct message chatchats/— list of all conversationsperfil/— own user profile (Instagram-style)perfil/[userId]/— public profile of any userperfil/[userId]/album/[albumId]/— read-only album view of another useramigos/— user searchcomunidades/— community listcomunidades/[communityId]/— group chat for a community
Auth flow: middleware.ts protects all / routes, refreshes the Supabase session on each request, and redirects unauthenticated users to /login. Registration sends a confirmation email — users must verify before logging in.
Supabase helpers:
lib/supabase/client.ts— browser client (use in Client Components)lib/supabase/server.ts— server client (use in Server Components / Server Actions)
Data model (key tables): profiles, albums, stickers, user_stickers, listings, listing_offers, listing_wants, trades, messages, follows, communities, community_messages, trade_reads, push_subscriptions. Migrations live in supabase/migrations/.
Trade matching logic: user_stickers.repeated_count > 0 = stickers the user can give. A sticker is "missing" for a user if they have no user_stickers row with owned = true for it. Marketplace (marketplace/page.tsx) fetches all users' repeated and owned stickers server-side, computes match counts per user, and passes pre-computed ProfileMatch[] to the client. Public profile (perfil/[userId]/page.tsx) does the same but also fetches full sticker details for the modal list.
Component pattern: Pages are async Server Components that fetch data from Supabase and pass it to *Client.tsx Client Components that own interactive state (modals, forms, chat). TypeScript interfaces for all entities are in lib/types/.
lib/albumCovers.ts— maps album names to local cover images inpublic/albums/. Add entries here when adding new album covers; no DB change needed.lib/importParser.ts— parses WhatsApp-style sticker list messages from other apps. ExportsparseImportTextandmatchStickers.
- Target is not
es2015, so never use[...new Set(...)]spread. Always useArray.from(new Set(...))instead. - Supabase Realtime channels: always use a unique channel name per component instance (e.g.
useRef(\channel-name-${Date.now()}`).current`) to avoid "cannot add callbacks after subscribe()" errors when effects re-run.
The app has two nav components that must stay in sync:
components/layout/Sidebar.tsx— desktop (md+)components/layout/BottomNav.tsx— mobile
Both receive userId from app/(protected)/layout.tsx for the unread badge.
All files in supabase/migrations/ must be run in order. Apply any that are not yet in production:
| File | What it adds |
|---|---|
009_add_trade_reads.sql |
trade_reads table + get_unread_count() RPC |
010_allow_direct_trades.sql |
trades.listing_id nullable for DMs |
011_add_reply_to_and_unread_per_trade.sql |
messages.reply_to_id + get_unread_counts_per_trade() RPC |
012_delete_policies.sql |
RLS: delete own messages and trades |
013_add_updated_at_to_user_stickers.sql |
user_stickers.updated_at + index for sorting |
014_add_notifications_seen_at.sql |
profiles.notifications_seen_at for notification badge |
015_push_subscriptions.sql |
push_subscriptions table for web push (VAPID) |