Skip to content

al3xb0/mindpal_task

Repository files navigation

Rick & Morty Favorites Dashboard

A Next.js application that allows users to explore Rick and Morty characters and save their favorites. Built with Supabase for authentication and database, using Edge Functions to fetch data from the Rick & Morty GraphQL API.

πŸš€ Tech Stack

  • Frontend: Next.js 16.1+ (App Router), React 19, TypeScript, Tailwind CSS 4
  • State Management: TanStack Query v5 (server state, optimistic updates, useInfiniteQuery with Load More)
  • Virtualization: TanStack Virtual v3 (windowed grid for large favorites lists)
  • Validation: Zod v4 (runtime schema validation)
  • Backend: Supabase (PostgreSQL + Auth)
  • Edge Functions: Deno (Supabase Edge Functions, in-memory caching)
  • API: Rick & Morty GraphQL API
  • Testing: Vitest + React Testing Library (unit/component), Playwright (E2E), Deno test (Edge Functions)

πŸ“ Project Structure

β”œβ”€β”€ src/                          # Frontend (Next.js)
β”‚   β”œβ”€β”€ app/                      # App Router pages
β”‚   β”‚   β”œβ”€β”€ dashboard/            # Characters list with Load More pagination
β”‚   β”‚   β”œβ”€β”€ favorites/            # User favorites with export
β”‚   β”‚   β”œβ”€β”€ login/                # Login page
β”‚   β”‚   β”œβ”€β”€ signup/               # Sign up page
β”‚   β”‚   └── not-found.tsx         # Custom 404 page
β”‚   β”œβ”€β”€ components/               # React components
β”‚   β”‚   β”œβ”€β”€ CharacterCard.tsx     # Character card (favorite + comparison buttons)
β”‚   β”‚   β”œβ”€β”€ CharacterModal.tsx    # Character details modal (accessible)
β”‚   β”‚   β”œβ”€β”€ CharacterFilters.tsx  # Search/filter controls (debounced, URL-synced)
β”‚   β”‚   β”œβ”€β”€ ComparisonModal.tsx   # Side-by-side character comparison (2–3 chars)
β”‚   β”‚   β”œβ”€β”€ ErrorBoundary.tsx     # Global error boundary
β”‚   β”‚   β”œβ”€β”€ Footer.tsx            # Global footer with author credit
β”‚   β”‚   β”œβ”€β”€ Loading.tsx           # Skeleton loaders
β”‚   β”‚   β”œβ”€β”€ Navbar.tsx            # Navigation with active links
β”‚   β”‚   β”œβ”€β”€ PasswordStrength.tsx  # Password requirements indicator
β”‚   β”‚   β”œβ”€β”€ Toast.tsx             # Toast notification system
β”‚   β”‚   └── icons/                # Reusable SVG icon components
β”‚   β”œβ”€β”€ lib/                      # Utilities & Supabase clients
β”‚   β”‚   β”œβ”€β”€ constants.ts          # Centralized constants
β”‚   β”‚   β”œβ”€β”€ imageLoadQueue.ts     # Concurrency-limited off-DOM preloader for CDN avatars
β”‚   β”‚   β”œβ”€β”€ queryKeys.ts          # Centralized TanStack Query keys
β”‚   β”‚   β”œβ”€β”€ hooks/                # Custom React hooks
β”‚   β”‚   β”‚   β”œβ”€β”€ useDebounce.ts    # Debounce hook for values/callbacks
β”‚   β”‚   β”‚   β”œβ”€β”€ useFavorites.ts   # Favorites with TanStack Query optimistic updates
β”‚   β”‚   β”‚   β”œβ”€β”€ useUrlFilters.ts  # URL-synced filter state (clean URLs)
β”‚   β”‚   β”‚   β”œβ”€β”€ useInfiniteCharactersQuery.ts # Paginated characters query (Load More)
β”‚   β”‚   β”‚   β”œβ”€β”€ useCurrentUser.ts # Auth user hook
β”‚   β”‚   β”‚   β”œβ”€β”€ useLock.ts        # Lock mechanism for async operations
β”‚   β”‚   β”‚   └── useThrottledImage.ts # Viewport-gated, concurrency-limited avatar loading
β”‚   β”‚   β”œβ”€β”€ providers.tsx         # QueryClientProvider wrapper
β”‚   β”‚   β”œβ”€β”€ schemas.ts            # Zod validation schemas
β”‚   β”‚   β”œβ”€β”€ logger.ts             # Structured logger
β”‚   β”‚   └── supabase/             # Supabase clients
β”‚   β”‚       β”œβ”€β”€ client.ts         # Browser client
β”‚   β”‚       β”œβ”€β”€ server.ts         # Server client
β”‚   β”‚       β”œβ”€β”€ middleware.ts     # Middleware client
β”‚   β”‚       └── hooks.ts          # useSupabase singleton hook
β”‚   β”œβ”€β”€ test/                     # Test setup
β”‚   β”‚   └── setup.ts              # Vitest global setup
β”‚   └── types/                    # TypeScript types
β”‚
β”œβ”€β”€ supabase/                     # Backend (Supabase)
β”‚   β”œβ”€β”€ functions/                # Edge Functions
β”‚   β”‚   └── get-characters/       # GraphQL proxy with validation + in-memory cache
β”‚   β”‚       β”œβ”€β”€ index.ts          # Function handler (exported for testing)
β”‚   β”‚       └── index.test.ts     # Deno unit tests
β”‚   └── migrations/               # SQL migrations
β”‚
β”œβ”€β”€ tests/                        # E2E tests
β”‚   └── e2e/
β”‚       β”œβ”€β”€ auth.spec.ts          # Auth flows (login, signup, redirect)
β”‚       └── favorites.spec.ts     # Favorites flows (add, remove, export, filter)
β”‚
β”œβ”€β”€ vitest.config.ts              # Vitest configuration
└── playwright.config.ts          # Playwright configuration

πŸ› οΈ Setup Instructions

Prerequisites

  • Node.js 18+
  • npm or yarn
  • Supabase account
  • Supabase CLI (optional, for local development)

1. Clone the repository

git clone <repository-url>
cd mindpal_task

2. Install dependencies

npm install

3. Set up Supabase

  1. Create a new project at supabase.com
  2. Go to Project Settings β†’ API to get your credentials
  3. Copy .env.example to .env.local and fill in the values:
cp .env.example .env.local
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key_here

4. Run database migrations

Navigate to your Supabase project's SQL Editor and run the migration file:

-- Copy contents from: supabase/migrations/create_favorite_characters.sql

This will create:

  • favorite_characters table
  • Row Level Security (RLS) policies
  • Necessary indexes

5. Deploy Edge Function

Using Supabase CLI:

# Login to Supabase (will open browser for authentication)
npx supabase login

# Link your local project to remote Supabase project
npx supabase link --project-ref <your-project-ref>

# Deploy the Edge Function (--no-verify-jwt makes it publicly accessible)
npx supabase functions deploy get-characters --no-verify-jwt

Note: The --no-verify-jwt flag is required because this function proxies a public API and doesn't need authentication. Without this flag, you'll get 401 Unauthorized errors.

You can find your project-ref in your Supabase Dashboard URL: https://supabase.com/dashboard/project/<project-ref>

Or manually via Supabase Dashboard:

  1. Go to Edge Functions in your Supabase Dashboard
  2. Create a new function named get-characters
  3. Copy the code from supabase/functions/get-characters/index.ts
  4. In function settings, disable "Verify JWT"

6. Configure Authentication

In your Supabase Dashboard:

  1. Go to Authentication β†’ Settings
  2. Enable Email/Password sign-in
  3. (Optional) Disable email confirmation for easier testing

7. Run the development server

npm run dev

Open http://localhost:3000 in your browser.

πŸ”‘ Environment Variables

Variable Description
NEXT_PUBLIC_SUPABASE_URL Your Supabase project URL
NEXT_PUBLIC_SUPABASE_ANON_KEY Your Supabase anonymous (anon) key
NEXT_PUBLIC_APP_URL (Optional) Canonical app URL for OG metadata
E2E_TEST_EMAIL (E2E only) Test account email
E2E_TEST_PASSWORD (E2E only) Test account password
PLAYWRIGHT_BASE_URL (E2E only) Base URL for Playwright (default: http://localhost:3000)

πŸ“Š Database Schema

favorite_characters

Column Type Description
id UUID Primary key
user_id UUID Foreign key to auth.users
character_id INT Rick & Morty character ID
character_name TEXT Character name
character_image TEXT Character image URL (optional)
character_status TEXT Character status (Alive/Dead/unknown)
character_species TEXT Character species
created_at TIMESTAMPTZ Creation timestamp

Constraints:

  • Unique constraint on (user_id, character_id)
  • RLS enabled: users can only access their own favorites

πŸ”’ Row Level Security Policies

  • SELECT: Users can only view their own favorites
  • INSERT: Users can only insert favorites for themselves
  • DELETE: Users can only delete their own favorites

🌐 Edge Function

The get-characters Edge Function acts as a proxy to the Rick & Morty GraphQL API:

  • Fetches paginated character data
  • Validates input parameters against a strict whitelist
  • In-memory caching (5-minute TTL) with X-Cache: HIT/MISS response header
  • Adds Cache-Control: public, s-maxage=300, stale-while-revalidate=60 for CDN caching
  • Handles errors gracefully
  • Returns formatted JSON response
  • Exports validateFilter and handleRequest for unit testing (Deno test)

Endpoint: POST /functions/v1/get-characters

Request body:

{
  "page": 1,
  "filter": {
    "name": "Rick",
    "status": "Alive"
  }
}

🎯 Key Technical Decisions

  1. GraphQL via Edge Function: All Rick & Morty API requests go through Supabase Edge Functions, not directly from the frontend. This provides:

    • Better security
    • Rate limiting control
    • Server-side error handling
    • Input validation (filter values validated against whitelist)
    • In-memory response caching (5-minute TTL, Cache-Control headers)
  2. TanStack Query v5: Server state managed with useQuery, useMutation, and useInfiniteQuery. Provides automatic cache invalidation, background refetching, and optimistic updates for favorites.

  3. Load More Pagination: Dashboard uses useInfiniteCharactersQuery (TanStack Query useInfiniteQuery) and appends the next page of 20 characters on an explicit Load More button click. Loading in user-driven batches keeps avatar requests well under the CDN's burst limit (vs. auto-fetch-on-scroll, which fired dozens of concurrent requests and tripped HTTP 429).

  4. URL-Synced Filters: useUrlFilters keeps filter state in URL search params. Empty params are removed to keep URLs clean. This enables deep linking and back-button support.

  5. Character Comparison: Select 2–3 characters using the compare button on each card. A floating action bar appears and opens ComparisonModal showing a side-by-side attribute table.

  6. Export Favorites: Favorites can be exported as JSON or CSV via a dropdown button. Pure client-side β€” uses Blob + URL.createObjectURL, no server round-trip.

  7. Virtualized Favorites Grid: When the favorites list exceeds 50 items, TanStack Virtual renders only visible rows, preventing DOM bloat.

  8. Supabase SSR: Using @supabase/ssr for proper server-side rendering and cookie-based authentication in Next.js App Router.

  9. Row Level Security: Database-level security ensures users can only access their own data, even if the client is compromised.

  10. Middleware Protection: Routes /dashboard and /favorites are protected at the middleware level, redirecting unauthenticated users to login.

  11. Debounced Filters: Search input uses debounce (300ms delay) to reduce API calls while typing.

  12. Accessibility: Modal components include focus trap, ARIA attributes, and keyboard navigation support.

  13. Throttled Avatar Loading: The Rick & Morty image CDN rate-limits by burst (~10 concurrent requests load cleanly, 20+ trip HTTP 429, and it stays banned while hammered). Avatars are served straight from the CDN (next/image with unoptimized, no optimizer proxy) and gated through useThrottledImage (IntersectionObserver, so only near-viewport images are requested) on top of imageLoadQueue β€” an off-DOM new Image() preloader with a concurrency cap of 6 that hands the visible <img> an already-cached URL (instant paint, no flicker). On a 429 a global circuit breaker pauses all fetching for a growing cooldown instead of retrying per-image, so the CDN ban window can clear.

πŸ“ Features

Core Features

  • βœ… User authentication (Sign up / Login)
  • βœ… Protected routes with middleware
  • βœ… Character listing with Load More pagination (loads next page on click)
  • βœ… Add/Remove favorites (optimistic updates via TanStack Query)
  • βœ… Favorites page with virtual grid (>50 items)
  • βœ… Row Level Security (users own data only)
  • βœ… Edge Function for GraphQL proxy with validation + in-memory caching

New Features

  • βœ… Load More β€” characters load in batches of 20 on an explicit button click
  • βœ… URL-synced Filters β€” name/status/species reflected in URL; empty params removed (clean URLs)
  • βœ… Character Comparison β€” select 2–3 characters for side-by-side comparison modal
  • βœ… Export Favorites β€” download your favorites list as JSON or CSV

UI/UX Features

  • βœ… Character Details Modal - Click on any card to see detailed info
  • βœ… Toast Notifications - Feedback on add/remove favorites
  • βœ… Active Navigation Links - Visual indication of current page
  • βœ… Custom 404 Page - Rick & Morty themed error page
  • βœ… Skeleton Loading - Animated loading cards
  • βœ… Password Strength Indicator - Visual password requirements
  • βœ… Responsive Design - Mobile-friendly
  • βœ… Dark Theme - Modern dark UI

Architecture Features

  • βœ… TanStack Query v5 β€” server state, cache, optimistic updates
  • βœ… TanStack Virtual v3 β€” windowed grid for large favorites lists
  • βœ… Zod Validation β€” runtime schema validation at boundaries
  • βœ… Error Boundary β€” Global error handling with recovery UI
  • βœ… Debounced Search β€” Auto-apply filters after 300ms typing pause
  • βœ… Operation Locking β€” Prevents double-click race conditions
  • βœ… Accessible Modals β€” Focus trap, ARIA attributes, keyboard navigation
  • βœ… Reusable Icons β€” SVG icon components library
  • βœ… Centralized Constants β€” No magic strings

πŸ–ΌοΈ Screenshots

Login Page

Login

Signup with Password Strength

Signup

Custom 404 Page

404

Dashboard

Characters grid with filters, Load More pagination, and clickable cards. Dashboard

Character Modal

Detailed character information including status, species, gender, origin, location, and episode count. Character Modal

Favorites

Personal collection with local search and status filter. Favorites

πŸ§ͺ Testing

# Unit and component tests (Vitest + React Testing Library)
npm test

# Run tests with coverage report
npm run test:coverage

# E2E tests (Playwright) β€” requires a running dev server or uses webServer config
npm run test:e2e

# Edge Function tests (Deno)
deno test supabase/functions/get-characters/index.test.ts

# Type-check and lint
npm run build
npm run lint

Test coverage includes:

  • Hook unit tests: useDebounce, useFavorites
  • Component tests: CharacterCard, CharacterFilters, CharacterModal
  • E2E auth flow: login, signup, redirect protection
  • E2E favorites flow: add/remove favorites, search, filter, export
  • Edge Function: validateFilter and handleRequest handler tests

πŸ”§ Supabase CLI Commands

# Login to Supabase
npx supabase login

# Link project (run once per project)
npx supabase link --project-ref <your-project-ref>

# Deploy Edge Function
npx supabase functions deploy get-characters --no-verify-jwt

# Push database migrations
npx supabase db push

# View function logs
npx supabase functions logs get-characters

πŸ“„ License

MIT

Releases

No releases published

Packages

 
 
 

Contributors