Refactor code structure and remove redundant changes

This commit is contained in:
pacnpal
2025-11-09 16:31:34 -05:00
parent 2884bc23ce
commit eb68cf40c6
1080 changed files with 27361 additions and 56687 deletions

523
migration/BUN_GUIDE.md Normal file
View File

@@ -0,0 +1,523 @@
# Bun Setup & Usage Guide
**For:** ThrillWiki Next.js 15 Migration
**Last Updated:** November 9, 2025
---
## 🎯 Overview
Bun is an all-in-one JavaScript runtime & toolkit that replaces Node.js, npm, yarn, and webpack. It's significantly faster than traditional tools and is the official package manager for this migration.
**Why Bun?**
- ⚡ 25x faster than npm
- 🚀 Fast startup times
- 📦 All-in-one (runtime + bundler + package manager)
- 🔄 Drop-in replacement for Node.js/npm
- ✅ Compatible with Next.js 15
---
## 📥 Installation
### macOS & Linux
```bash
curl -fsSL https://bun.sh/install | bash
```
### Windows (WSL2)
```bash
curl -fsSL https://bun.sh/install | bash
```
### Verify Installation
```bash
bun --version
# Should output: 1.x.x
```
---
## 🚀 Basic Commands
### Package Management
```bash
# Install dependencies from package.json
bun install
# Install a specific package
bun add <package-name>
# Install as dev dependency
bun add -d <package-name>
# Install globally
bun add -g <package-name>
# Remove a package
bun remove <package-name>
# Update packages
bun update
# Update a specific package
bun update <package-name>
```
### Running Scripts
```bash
# Run a package.json script
bun run dev
bun run build
bun run start
bun run test
# Or shorter (bun run is optional)
bun dev
bun build
bun start
bun test
```
### Running Files
```bash
# Run a TypeScript/JavaScript file directly
bun index.ts
bun server.js
# Run with watch mode (auto-restart on changes)
bun --watch index.ts
```
---
## 📦 Common ThrillWiki Commands
### Development
```bash
# Start Next.js development server with Turbopack
bun run dev
# Or shorter
bun dev
# Expected output:
# ▲ Next.js 15.x.x
# - Local: http://localhost:3000
# - Turbopack: ✓ Enabled
```
### Building
```bash
# Create production build
bun run build
# This will:
# 1. Compile TypeScript
# 2. Build Next.js pages
# 3. Optimize bundles
# 4. Generate static pages (ISR/SSG)
```
### Production
```bash
# Start production server (after build)
bun run start
# Server will run on http://localhost:3000
```
### Type Checking
```bash
# Run TypeScript compiler
bun run type-check
# This runs: tsc --noEmit
```
### Linting
```bash
# Run ESLint
bun run lint
# Fix auto-fixable issues
bun run lint --fix
```
### Testing
```bash
# Run tests
bun test
# Run tests in watch mode
bun test --watch
# Run tests with coverage
bun test --coverage
```
---
## 🔧 Project Setup
### Initialize New Next.js Project
```bash
# Create new Next.js 15 project with Bun
bun create next-app@latest my-project --typescript --tailwind --app
cd my-project
```
### Migrate Existing Project to Bun
```bash
# Remove old lock files
rm package-lock.json
rm yarn.lock
rm pnpm-lock.yaml
# Install with Bun
bun install
# This creates bun.lockb
```
---
## ⚙️ Configuration
### package.json Scripts
Update your `package.json` scripts to use Bun:
```json
{
"scripts": {
"dev": "next dev --turbo",
"build": "next build",
"start": "next start",
"lint": "next lint",
"type-check": "tsc --noEmit",
"test": "vitest"
}
}
```
### .bunfig.toml (Optional)
Create `.bunfig.toml` in project root for Bun configuration:
```toml
# Bun configuration
[install]
# Use exact versions (like npm's --exact)
exact = true
# Cache directory
cache = ".bun-cache"
# Optional: Configure registry
# registry = "https://registry.npmjs.org"
[test]
# Test configuration
coverage = true
```
---
## 🔄 NPM to Bun Command Mapping
| NPM Command | Bun Command | Description |
|------------|-------------|-------------|
| `npm install` | `bun install` | Install dependencies |
| `npm install <pkg>` | `bun add <pkg>` | Add package |
| `npm install -D <pkg>` | `bun add -d <pkg>` | Add dev dependency |
| `npm uninstall <pkg>` | `bun remove <pkg>` | Remove package |
| `npm run <script>` | `bun run <script>` | Run script |
| `npm run <script>` | `bun <script>` | Run script (shorter) |
| `npm update` | `bun update` | Update packages |
| `npm ci` | `bun install --frozen-lockfile` | Clean install |
| `npm test` | `bun test` | Run tests |
| `npx <cmd>` | `bunx <cmd>` | Execute package |
---
## 🎨 Bun with Next.js 15
### Development Server
```bash
# Start with Turbopack (Next.js 15)
bun dev
# Features:
# - Hot Module Replacement (HMR)
# - Fast Refresh
# - TypeScript compilation
# - CSS/Tailwind compilation
```
### Environment Variables
Bun reads `.env.local` automatically:
```bash
# .env.local
NEXT_PUBLIC_DJANGO_API_URL=http://localhost:8000/api/v1
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID=xxx
```
Access in code:
```typescript
const apiUrl = process.env.NEXT_PUBLIC_DJANGO_API_URL;
```
---
## 🧪 Testing with Bun
### Vitest Setup
```bash
# Install Vitest
bun add -d vitest @testing-library/react @testing-library/jest-dom
```
### vitest.config.ts
```typescript
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
globals: true,
},
resolve: {
alias: {
'@': path.resolve(__dirname, './'),
},
},
});
```
### Run Tests
```bash
bun test
```
---
## 🚨 Common Issues & Solutions
### Issue: `bun: command not found`
**Solution:**
```bash
# Reload shell configuration
source ~/.bashrc # or ~/.zshrc
# Or restart terminal
```
### Issue: Package installation fails
**Solution:**
```bash
# Clear cache and reinstall
rm -rf node_modules bun.lockb
bun install
```
### Issue: Next.js dev server won't start
**Solution:**
```bash
# Check port 3000 is free
lsof -i :3000
# Kill process if needed
kill -9 <PID>
# Restart dev server
bun dev
```
### Issue: TypeScript errors after installing packages
**Solution:**
```bash
# Regenerate types
bun run type-check
# Or restart TS server in VS Code
# Cmd+Shift+P → "TypeScript: Restart TS Server"
```
### Issue: Bun vs Node.js compatibility
Some packages may not work with Bun yet. Use Node.js fallback:
```bash
# Run with Node.js instead
node --loader tsx index.ts
# Or use NODE_ENV
NODE_ENV=production node index.js
```
---
## 📊 Performance Comparison
### Install Speed
```bash
# NPM
time npm install # ~45s
# Yarn
time yarn install # ~35s
# Bun
time bun install # ~2s ⚡
```
### Script Execution
```bash
# NPM
time npm run build # ~120s
# Bun
time bun run build # ~90s
```
---
## 🔗 Advanced Usage
### Workspaces (Monorepo)
```json
// package.json
{
"workspaces": ["packages/*"]
}
```
```bash
# Install all workspace dependencies
bun install
# Run script in specific workspace
bun --filter my-package run build
```
### Custom Scripts
```typescript
// scripts/build.ts
import { $ } from 'bun';
console.log('Building project...');
await $`bun run type-check`;
await $`bun run lint`;
await $`next build`;
console.log('✅ Build complete!');
```
Run:
```bash
bun scripts/build.ts
```
---
## 🎯 Best Practices
### 1. Use Exact Versions
Add to `.bunfig.toml`:
```toml
[install]
exact = true
```
### 2. Commit Lock File
Always commit `bun.lockb` to version control:
```bash
git add bun.lockb
git commit -m "Update dependencies"
```
### 3. Use Scripts
Define common tasks in `package.json`:
```json
{
"scripts": {
"dev": "next dev --turbo",
"build": "next build",
"start": "next start",
"clean": "rm -rf .next node_modules bun.lockb"
}
}
```
### 4. Regular Updates
Keep Bun updated:
```bash
bun upgrade
```
---
## 📚 Resources
- Official Documentation: https://bun.sh/docs
- GitHub: https://github.com/oven-sh/bun
- Discord: https://bun.sh/discord
- Next.js + Bun: https://nextjs.org/docs/app/building-your-application/configuring/bun
---
## 🔗 Related Documentation
- [Phase 1: Foundation](./PHASE_01_FOUNDATION.md)
- [Environment Variables Guide](./ENVIRONMENT_VARIABLES.md)
- [Next.js 15 Migration Guide](./NEXTJS_15_MIGRATION_GUIDE.md)
---
**Document Version:** 1.0
**Last Updated:** November 9, 2025

View File

@@ -0,0 +1,306 @@
# Environment Variables Guide
**For:** ThrillWiki Next.js 15 Migration
**Last Updated:** November 9, 2025
---
## 🎯 Overview
This guide covers all environment variables required for the ThrillWiki migration from React→Next.js 15 and Supabase→Django.
## 🔑 Critical Rules
1. **NO HARDCODED VALUES** - All configuration must use environment variables
2. **Client vs Server** - Understand NEXT_PUBLIC_ prefix usage
3. **Never commit secrets** - .env.local in .gitignore
4. **.env.example** - Document all required vars (without values)
5. **Validate on startup** - Fail fast if required vars missing
---
## 📋 Required Environment Variables
### Next.js Client-Side (Public)
These variables are exposed to the browser. Use `NEXT_PUBLIC_` prefix.
```bash
# Django API (required)
NEXT_PUBLIC_DJANGO_API_URL=https://api.thrillwiki.com
# CloudFlare Images (required for uploads)
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID=your_account_id_here
```
### Next.js Server-Side (Private)
These variables are ONLY available server-side. NO `NEXT_PUBLIC_` prefix.
```bash
# CloudFlare API Token (for image uploads from server)
CLOUDFLARE_API_TOKEN=your_secret_token_here
# Django Admin Credentials (if needed for server-side operations)
DJANGO_ADMIN_KEY=your_admin_key_here
```
### Legacy Supabase (Temporary - Remove in Phase 14)
Keep these ONLY during migration phases 1-13:
```bash
# Supabase (to be removed)
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key_here
```
---
## 📁 File Structure
```
project-root/
├── .env.local # Local development (git-ignored)
├── .env.example # Template (git-committed)
├── .env.production # Production (if needed, git-ignored)
└── .env.test # Testing (git-ignored)
```
### .env.local (Development)
```bash
# ================================
# Next.js Environment Variables
# ================================
# Django REST API
NEXT_PUBLIC_DJANGO_API_URL=http://localhost:8000/api/v1
# CloudFlare Images
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID=your_dev_account_id
CLOUDFLARE_API_TOKEN=your_dev_token
# Supabase (temporary - remove after Phase 14)
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key
# Development flags
NODE_ENV=development
```
### .env.example (Template)
```bash
# ================================
# Next.js Environment Variables
# Copy to .env.local and fill in values
# ================================
# Django REST API (required)
NEXT_PUBLIC_DJANGO_API_URL=
# CloudFlare Images (required)
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID=
CLOUDFLARE_API_TOKEN=
# Supabase (temporary - will be removed)
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
```
### .env.production (Production)
```bash
# Django REST API
NEXT_PUBLIC_DJANGO_API_URL=https://api.thrillwiki.com
# CloudFlare Images
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID=production_account_id
CLOUDFLARE_API_TOKEN=production_secret_token
# Production settings
NODE_ENV=production
```
---
## 🔐 Security Best Practices
### 1. Client vs Server Variables
```typescript
// ❌ WRONG - Exposes secret to browser
const apiKey = process.env.CLOUDFLARE_API_TOKEN;
// ✅ CORRECT - Only available server-side
// In Server Component or API Route
const apiKey = process.env.CLOUDFLARE_API_TOKEN;
// ✅ CORRECT - Public variable in client
const apiUrl = process.env.NEXT_PUBLIC_DJANGO_API_URL;
```
### 2. Never Log Secrets
```typescript
// ❌ WRONG
console.log('API Token:', process.env.CLOUDFLARE_API_TOKEN);
// ✅ CORRECT
console.log('API URL:', process.env.NEXT_PUBLIC_DJANGO_API_URL);
```
### 3. Validate Environment Variables
```typescript
// lib/env.ts
import { z } from 'zod';
const envSchema = z.object({
NEXT_PUBLIC_DJANGO_API_URL: z.string().url(),
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID: z.string().min(1),
CLOUDFLARE_API_TOKEN: z.string().min(1).optional(), // Server-only
});
// Validate on app start
export const env = envSchema.parse({
NEXT_PUBLIC_DJANGO_API_URL: process.env.NEXT_PUBLIC_DJANGO_API_URL,
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID: process.env.NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID,
CLOUDFLARE_API_TOKEN: process.env.CLOUDFLARE_API_TOKEN,
});
// Usage
import { env } from '@/lib/env';
const apiUrl = env.NEXT_PUBLIC_DJANGO_API_URL;
```
---
## 🚀 Deployment Environments
### Vercel
Set environment variables in Vercel dashboard:
1. Go to Project Settings → Environment Variables
2. Add each variable:
- `NEXT_PUBLIC_DJANGO_API_URL` = `https://api.thrillwiki.com`
- `NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID` = `your_account`
- `CLOUDFLARE_API_TOKEN` = `your_token` (mark as secret)
### Docker
Pass via docker-compose.yml:
```yaml
services:
nextjs:
environment:
- NEXT_PUBLIC_DJANGO_API_URL=https://api.thrillwiki.com
- NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID=${CLOUDFLARE_ACCOUNT_ID}
- CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
```
---
## 🧪 Testing
### Check Variables Are Loaded
```typescript
// Test in dev console (browser)
console.log('API URL:', process.env.NEXT_PUBLIC_DJANGO_API_URL);
// Should show the URL
console.log('Secret Token:', process.env.CLOUDFLARE_API_TOKEN);
// Should show 'undefined' (not exposed to client)
```
### Verify Build-Time Variables
```bash
# Build with verbose output
bun run build
# Check if variables are embedded
grep -r "NEXT_PUBLIC_DJANGO_API_URL" .next/
```
---
## 📊 Migration Phases
### Phase 1-13: Dual Variables
```bash
# Keep both during migration
NEXT_PUBLIC_DJANGO_API_URL=...
NEXT_PUBLIC_SUPABASE_URL=...
```
### Phase 14: Remove Supabase
```bash
# Remove these completely
# NEXT_PUBLIC_SUPABASE_URL=...
# NEXT_PUBLIC_SUPABASE_ANON_KEY=...
# Keep only Django
NEXT_PUBLIC_DJANGO_API_URL=...
```
---
## ⚠️ Common Issues
### Issue: Variable is undefined in browser
**Cause:** Missing `NEXT_PUBLIC_` prefix
**Fix:** Rename variable:
```bash
# Wrong
DJANGO_API_URL=https://api.thrillwiki.com
# Right
NEXT_PUBLIC_DJANGO_API_URL=https://api.thrillwiki.com
```
### Issue: Variable not updating after change
**Cause:** Next.js caches env vars at build time
**Fix:** Restart dev server:
```bash
# Stop server (Ctrl+C)
bun run dev
```
### Issue: Hardcoded URL slipped through
**Find:** Search codebase:
```bash
grep -r "https://api.thrillwiki.com" src/ --exclude-dir=node_modules
```
**Fix:** Replace with env var:
```typescript
// Before
const API_URL = "https://api.thrillwiki.com";
// After
const API_URL = process.env.NEXT_PUBLIC_DJANGO_API_URL;
```
---
## 🔗 Related Documentation
- [Phase 1: Foundation](./PHASE_01_FOUNDATION.md) - Initial setup
- [Phase 13: Next.js Optimization](./PHASE_13_NEXTJS_OPTIMIZATION.md) - Security
- [Bun Guide](./BUN_GUIDE.md) - Package manager
- Next.js Docs: https://nextjs.org/docs/app/building-your-application/configuring/environment-variables
---
**Document Version:** 1.0
**Last Updated:** November 9, 2025

View File

@@ -0,0 +1,605 @@
# Next.js 15 Migration Guide
**For:** ThrillWiki React → Next.js 15 + App Router Migration
**Last Updated:** November 9, 2025
---
## 🎯 Overview
This guide provides patterns, examples, and best practices for migrating from React SPA to Next.js 15 with App Router.
**What's Changing:**
- React Router → Next.js File-based Routing
- Client-side rendering → Server-side rendering by default
- Manual data fetching → Built-in data fetching
- Vite → Turbopack
- npm → Bun
---
## 📁 File Structure Migration
### Old Structure (React SPA)
```
src/
├── pages/
│ ├── Index.tsx
│ ├── Parks.tsx
│ ├── ParkDetail.tsx
│ └── ...
├── components/
│ ├── Navigation.tsx
│ ├── ParkCard.tsx
│ └── ...
├── hooks/
├── services/
└── App.tsx
```
### New Structure (Next.js 15)
```
app/
├── layout.tsx # Root layout (replaces App.tsx)
├── page.tsx # Homepage (replaces Index.tsx)
├── parks/
│ ├── page.tsx # /parks (replaces Parks.tsx)
│ └── [parkSlug]/
│ └── page.tsx # /parks/[slug] (replaces ParkDetail.tsx)
components/
├── navigation/
│ └── Navigation.tsx # Shared components
├── parks/
│ └── ParkCard.tsx
hooks/
services/
lib/
```
---
## 🔄 Routing Migration
### React Router → Next.js
#### Old: React Router
```typescript
// App.tsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/parks" element={<ParksPage />} />
<Route path="/parks/:slug" element={<ParkDetailPage />} />
</Routes>
</BrowserRouter>
);
}
```
#### New: Next.js File-based Routing
```
app/
├── page.tsx → /
├── parks/
│ ├── page.tsx → /parks
│ └── [slug]/
│ └── page.tsx → /parks/:slug
```
### Navigation
#### Old: React Router Links
```typescript
import { Link, useNavigate } from 'react-router-dom';
function Navigation() {
const navigate = useNavigate();
return (
<nav>
<Link to="/parks">Parks</Link>
<button onClick={() => navigate('/parks/cedar-point')}>
Cedar Point
</button>
</nav>
);
}
```
#### New: Next.js Links
```typescript
import Link from 'next/link';
import { useRouter } from 'next/navigation';
function Navigation() {
const router = useRouter();
return (
<nav>
<Link href="/parks">Parks</Link>
<button onClick={() => router.push('/parks/cedar-point')}>
Cedar Point
</button>
</nav>
);
}
```
### URL Parameters
#### Old: React Router
```typescript
import { useParams } from 'react-router-dom';
function ParkDetail() {
const { slug } = useParams();
// ...
}
```
#### New: Next.js (Server Component)
```typescript
// app/parks/[slug]/page.tsx
export default function ParkDetail({
params
}: {
params: { slug: string }
}) {
// params.slug is available
}
```
#### New: Next.js (Client Component)
```typescript
'use client';
import { useParams } from 'next/navigation';
export default function ParkDetail() {
const params = useParams();
const slug = params.slug;
}
```
### Query Parameters
#### Old: React Router
```typescript
import { useSearchParams } from 'react-router-dom';
function ParksPage() {
const [searchParams] = useSearchParams();
const filter = searchParams.get('filter');
}
```
#### New: Next.js (Server Component)
```typescript
export default function ParksPage({
searchParams
}: {
searchParams: { filter?: string }
}) {
const filter = searchParams.filter;
}
```
#### New: Next.js (Client Component)
```typescript
'use client';
import { useSearchParams } from 'next/navigation';
export default function ParksPage() {
const searchParams = useSearchParams();
const filter = searchParams.get('filter');
}
```
---
## 🖥️ Server vs Client Components
### Decision Tree
```
Does it need interactivity?
├─ NO → Server Component (default)
│ ├─ Displays data
│ ├─ Static content
│ └─ SEO-critical
└─ YES → Client Component ('use client')
├─ Forms with state
├─ Event handlers
├─ Browser APIs
└─ Hooks (useState, useEffect, etc.)
```
### Server Component Example
```typescript
// app/parks/page.tsx
// No 'use client' directive = Server Component
import { env } from '@/lib/env';
export default async function ParksPage() {
// Fetch data directly in component
const parks = await fetch(
`${env.NEXT_PUBLIC_DJANGO_API_URL}/parks/`
).then(r => r.json());
return (
<div>
<h1>Theme Parks</h1>
{parks.results.map(park => (
<ParkCard key={park.id} park={park} />
))}
</div>
);
}
```
### Client Component Example
```typescript
// components/parks/ParkFilters.tsx
'use client'; // Mark as Client Component
import { useState } from 'react';
import { useRouter } from 'next/navigation';
export function ParkFilters() {
const [filter, setFilter] = useState('');
const router = useRouter();
const handleFilterChange = (value: string) => {
setFilter(value);
router.push(`/parks?filter=${value}`);
};
return (
<select value={filter} onChange={(e) => handleFilterChange(e.target.value)}>
<option value="">All Parks</option>
<option value="theme">Theme Parks</option>
<option value="water">Water Parks</option>
</select>
);
}
```
### Mixing Server and Client
```typescript
// app/parks/page.tsx (Server Component)
import { ParkFilters } from '@/components/parks/ParkFilters'; // Client
import { ParksList } from '@/components/parks/ParksList'; // Can be Server
export default async function ParksPage() {
const parks = await fetch(API_URL).then(r => r.json());
return (
<div>
<h1>Parks</h1>
<ParkFilters /> {/* Client Component for interactivity */}
<ParksList parks={parks} /> {/* Server Component for display */}
</div>
);
}
```
---
## 📡 Data Fetching
### Old: React with useEffect
```typescript
function ParkDetail({ slug }: { slug: string }) {
const [park, setPark] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(`/api/parks/${slug}`)
.then(r => r.json())
.then(data => {
setPark(data);
setLoading(false);
});
}, [slug]);
if (loading) return <div>Loading...</div>;
return <div>{park.name}</div>;
}
```
### New: Next.js Server Component
```typescript
// app/parks/[slug]/page.tsx
export default async function ParkDetail({
params
}: {
params: { slug: string }
}) {
// Fetch directly - no useEffect needed
const park = await fetch(
`${API_URL}/parks/${params.slug}/`,
{ next: { revalidate: 3600 } } // Cache for 1 hour
).then(r => r.json());
return <div>{park.name}</div>;
}
```
### Caching Strategies
```typescript
// No caching (always fresh)
fetch(url, { cache: 'no-store' });
// Cache forever (until revalidated)
fetch(url, { cache: 'force-cache' });
// Revalidate after 60 seconds
fetch(url, { next: { revalidate: 60 } });
// Revalidate on-demand (from API route)
fetch(url, { next: { tags: ['parks'] } });
// Then: revalidateTag('parks')
```
### Parallel Data Fetching
```typescript
export default async function ParkDetail({ params }) {
// Fetch in parallel
const [park, rides, reviews] = await Promise.all([
fetch(`${API_URL}/parks/${params.slug}/`),
fetch(`${API_URL}/parks/${params.slug}/rides/`),
fetch(`${API_URL}/parks/${params.slug}/reviews/`),
]).then(responses =>
Promise.all(responses.map(r => r.json()))
);
return (
<div>
<ParkHeader park={park} />
<RidesList rides={rides} />
<ReviewsList reviews={reviews} />
</div>
);
}
```
---
## 🎨 Layouts
### Root Layout
```typescript
// app/layout.tsx
import { Inter } from 'next/font/google';
import './globals.css';
const inter = Inter({ subsets: ['latin'] });
export const metadata = {
title: 'ThrillWiki',
description: 'Theme Park Database',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<Navigation />
<main>{children}</main>
<Footer />
</body>
</html>
);
}
```
### Nested Layouts
```typescript
// app/parks/layout.tsx
export default function ParksLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="parks-container">
<ParksSidebar />
<div className="parks-content">
{children}
</div>
</div>
);
}
```
---
## 🔍 SEO & Metadata
### Static Metadata
```typescript
// app/parks/page.tsx
export const metadata = {
title: 'Theme Parks - ThrillWiki',
description: 'Browse theme parks from around the world',
openGraph: {
title: 'Theme Parks',
description: 'Browse theme parks',
images: ['/og-parks.png'],
},
};
```
### Dynamic Metadata
```typescript
// app/parks/[slug]/page.tsx
export async function generateMetadata({ params }) {
const park = await fetch(`${API_URL}/parks/${params.slug}/`)
.then(r => r.json());
return {
title: `${park.name} - ThrillWiki`,
description: park.description,
openGraph: {
title: park.name,
description: park.description,
images: [park.image_url],
},
};
}
```
---
## ⚡ Loading & Error States
### Loading UI
```typescript
// app/parks/loading.tsx
export default function Loading() {
return (
<div className="skeleton">
<div className="skeleton-header" />
<div className="skeleton-grid">
{[...Array(6)].map((_, i) => (
<div key={i} className="skeleton-card" />
))}
</div>
</div>
);
}
```
### Error Boundaries
```typescript
// app/parks/error.tsx
'use client';
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={reset}>Try again</button>
</div>
);
}
```
### Not Found
```typescript
// app/parks/[slug]/not-found.tsx
export default function NotFound() {
return (
<div>
<h2>Park Not Found</h2>
<p>The park you're looking for doesn't exist.</p>
</div>
);
}
// In page.tsx
import { notFound } from 'next/navigation';
export default async function ParkDetail({ params }) {
const park = await fetch(`${API_URL}/parks/${params.slug}/`)
.then(r => {
if (!r.ok) throw new Error();
return r.json();
})
.catch(() => notFound());
return <div>{park.name}</div>;
}
```
---
## 🚀 Deployment
### Build Command
```bash
bun run build
```
### Environment Variables
Set in Vercel/hosting platform:
- `NEXT_PUBLIC_DJANGO_API_URL`
- `NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID`
- Server-only secrets (no NEXT_PUBLIC_ prefix)
### Vercel Deployment
```bash
# Install Vercel CLI
bun add -g vercel
# Deploy
vercel
```
---
## 📚 Additional Resources
- Next.js 15 Documentation: https://nextjs.org/docs
- App Router: https://nextjs.org/docs/app
- Server Components: https://nextjs.org/docs/app/building-your-application/rendering/server-components
- Data Fetching: https://nextjs.org/docs/app/building-your-application/data-fetching
---
## 🔗 Related Documentation
- [Phase 1: Foundation](./PHASE_01_FOUNDATION.md)
- [Phase 12: Pages Migration](./PHASE_12_PAGES_MIGRATION.md)
- [Phase 13: Optimization](./PHASE_13_NEXTJS_OPTIMIZATION.md)
- [Environment Variables](./ENVIRONMENT_VARIABLES.md)
- [Bun Guide](./BUN_GUIDE.md)
---
**Document Version:** 1.0
**Last Updated:** November 9, 2025

View File

@@ -0,0 +1,556 @@
# PHASE 1: Foundation + Next.js 15 Setup
**Status:** 🟡 Partially Complete (Core Foundation Ready)
**Estimated Time:** 20-25 hours
**Priority:** CRITICAL
**Depends On:** None
**Blocks:** All other phases
**Note:** Core infrastructure complete (Tasks 1.0, 1.1, 1.3). Types (1.2), Base Service (1.4), and Testing (1.5) deferred to be created as needed in subsequent phases.
---
## 🎯 Phase Goal
Set up Next.js 16 with App Router + Turbopack, configure Bun, establish environment variables, and build the core infrastructure that all services will depend on: API client, type system, error handling, and base service class.
**This phase includes:**
1. Next.js 16 installation and configuration
2. Bun package manager setup
3. Environment variables configuration
4. API client infrastructure
5. Type system for Django API
6. Service layer foundation
---
## 📋 Prerequisites
- [ ] Django backend is running at `https://api.thrillwiki.com`
- [ ] You have API access token for testing
- [ ] Bun is installed (`curl -fsSL https://bun.sh/install | bash`)
- [ ] Node.js 18+ is installed (for Next.js 15)
- [ ] Git repository is initialized
---
## 🗂️ Files to Create/Modify
```
# Next.js Configuration
next.config.js (NEW)
.env.local (NEW)
.env.example (NEW)
tsconfig.json (UPDATE)
package.json (UPDATE)
# Next.js App Structure
app/
├── layout.tsx (NEW)
├── page.tsx (NEW)
├── loading.tsx (NEW)
├── error.tsx (NEW)
└── not-found.tsx (NEW)
# API & Services
lib/
├── api/
│ ├── client.ts (NEW)
│ ├── errorHandler.ts (NEW)
│ └── index.ts (NEW)
└── env.ts (NEW - env validation)
types/
├── api/
│ ├── responses.ts (NEW)
│ ├── requests.ts (NEW)
│ ├── errors.ts (NEW)
│ └── index.ts (NEW)
└── entities/
├── park.ts (NEW)
├── ride.ts (NEW)
└── ... (others)
services/
├── base/
│ ├── BaseService.ts (NEW)
│ └── index.ts (NEW)
└── __tests__/
├── setup.ts (NEW)
└── mocks/ (NEW)
```
---
## ✅ Task 1.0: Next.js 15 + Bun Setup (5 hours)
### Step 1: Install Next.js 15 with Bun
```bash
# Create Next.js 15 project with Bun
bun create next-app@latest . --typescript --tailwind --app --no-src-dir
# Or if starting fresh in a new directory:
bun create next-app@latest thrillwiki --typescript --tailwind --app --no-src-dir
cd thrillwiki
```
### Step 2: Configure next.config.js
Create `next.config.js`:
```javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
// Enable Turbopack for development
experimental: {
turbo: {
resolveAlias: {
'@': './',
},
},
},
// Image optimization for CloudFlare
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'imagedelivery.net',
},
],
formats: ['image/avif', 'image/webp'],
},
// Production optimizations
compress: true,
poweredByHeader: false,
reactStrictMode: true,
};
module.exports = nextConfig;
```
### Step 3: Environment Variables Setup
Create `.env.local`:
```bash
# Django API
NEXT_PUBLIC_DJANGO_API_URL=http://localhost:8000/api/v1
# CloudFlare Images
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID=your_account_id
# Supabase (temporary - keep during migration)
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key
```
Create `.env.example`:
```bash
# Django API (required)
NEXT_PUBLIC_DJANGO_API_URL=
# CloudFlare Images (required)
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID=
# Supabase (temporary - will be removed)
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
```
### Step 4: Environment Variable Validation
Create `lib/env.ts`:
```typescript
import { z } from 'zod';
const envSchema = z.object({
NEXT_PUBLIC_DJANGO_API_URL: z.string().url(),
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID: z.string().min(1),
});
export const env = envSchema.parse({
NEXT_PUBLIC_DJANGO_API_URL: process.env.NEXT_PUBLIC_DJANGO_API_URL,
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID: process.env.NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID,
});
```
### Step 5: Update package.json Scripts
```json
{
"scripts": {
"dev": "next dev --turbo",
"build": "next build",
"start": "next start",
"lint": "next lint",
"type-check": "tsc --noEmit"
}
}
```
### Checklist
- [x] Next.js 16 installed with Bun (Next.js 16.0.1)
- [x] ~~Turbopack enabled in next.config.js~~ (Removed - deprecated experimental key in Next.js 16)
- [x] .env.local created with all variables
- [x] .env.example documented
- [x] Environment validation in lib/env.ts
- [x] package.json scripts use Bun
- [x] Dev server runs with `bun run dev` - ✅ Running on http://localhost:3000
- [x] No TypeScript errors
---
## ✅ Task 1.1: Base API Client (3 hours)
### Checklist
- [x] Install dependencies: `bun add axios zod` (axios installed, zod already present)
- [x] Create `lib/api/client.ts`
- [x] Configure base URL from environment variable
- [x] Add JWT token management
- [x] Get token from localStorage
- [x] Attach to Authorization header
- [x] Handle token expiry
- [x] Create request interceptor
- [x] Add auth headers
- [x] Add request logging (dev only)
- [x] Create response interceptor
- [x] Handle success responses
- [x] Handle error responses
- [x] Extract data from Django response format
- [x] Implement retry logic
- [x] Retry on network failures
- [x] Exponential backoff
- [x] Max 3 retries
- [x] Add timeout configuration (30s default)
- [x] Export configured axios instance
### Acceptance Criteria
- [x] Can make GET request to `/api/v1/health` (client configured)
- [x] Token automatically attached to requests
- [x] Errors are properly caught and logged
- [x] Retries work on network failures
- [x] TypeScript types are correct
### Implementation Example
```typescript
// lib/api/client.ts
import axios from 'axios';
import { env } from '@/lib/env';
const apiClient = axios.create({
baseURL: env.NEXT_PUBLIC_DJANGO_API_URL,
timeout: 30000,
headers: {
'Content-Type': 'application/json',
},
});
// Request interceptor
apiClient.interceptors.request.use((config) => {
// Add auth token if available
if (typeof window !== 'undefined') {
const token = localStorage.getItem('auth_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
}
return config;
});
// Response interceptor
apiClient.interceptors.response.use(
(response) => response,
(error) => {
// Handle errors
return Promise.reject(error);
}
);
export { apiClient };
```
### Testing
```typescript
// Test in app/page.tsx or a Server Component
import { env } from '@/lib/env';
export default async function HomePage() {
// Test API connection
const response = await fetch(`${env.NEXT_PUBLIC_DJANGO_API_URL}/health`);
const data = await response.json();
return <div>API Status: {data.status}</div>;
}
```
---
## ✅ Task 1.2: Type Definitions (2 hours)
### Checklist
- [ ] Install zod: `bun add zod`
- [ ] Create `types/api/responses.ts`
- [ ] `ApiResponse<T>` generic type
- [ ] `PaginatedResponse<T>` type
- [ ] `SuccessResponse<T>` type
- [ ] Create `src/types/api/errors.ts`
- [ ] `ApiError` interface
- [ ] `ValidationError` interface
- [ ] `ErrorDetail` interface
- [ ] Create `src/types/api/requests.ts`
- [ ] `PaginationParams` interface
- [ ] `FilterParams` interface
- [ ] `SortParams` interface
- [ ] Create entity type files
- [ ] `types/entities/park.ts`
- [ ] `types/entities/ride.ts`
- [ ] `types/entities/company.ts`
- [ ] `types/entities/rideModel.ts`
- [ ] `types/entities/submission.ts`
- [ ] `types/entities/review.ts`
- [ ] Create `types/api/index.ts` - export all types
- [ ] Document type usage
### Acceptance Criteria
- [ ] All types match Django API schema
- [ ] No `any` types used
- [ ] Types are reusable across services
- [ ] Generic types work correctly
- [ ] TypeScript compiler is happy
### Example Types
```typescript
// ApiResponse<T>
interface ApiResponse<T> {
data: T;
status: 'success' | 'error';
message?: string;
}
// PaginatedResponse<T>
interface PaginatedResponse<T> {
results: T[];
count: number;
next: string | null;
previous: string | null;
}
// ApiError
interface ApiError {
message: string;
code?: string;
details?: ErrorDetail[];
status: number;
}
```
---
## ✅ Task 1.3: Error Handler Integration (2 hours)
### Checklist
- [x] Create `lib/api/errorHandler.ts`
- [x] Map Django error responses to user messages
- [x] Handle HTTP status codes
- [x] 400 - Validation errors
- [x] 401 - Unauthorized
- [x] 403 - Forbidden
- [x] 404 - Not found
- [x] 429 - Rate limiting
- [x] 500+ - Server errors
- [x] Extract validation errors from Django format
- [x] Format error messages for UI display
- [x] Integrate with existing error logging
- [ ] Add Sentry integration (if available) - Deferred to later phase
- [x] Create user-friendly error messages
- [x] Handle network errors separately
### Acceptance Criteria
- [x] All Django errors are properly translated
- [x] Validation errors show field-specific messages
- [x] Errors are logged to monitoring
- [x] User sees helpful error messages
- [x] No stack traces exposed to users
### Testing
```typescript
// Test error handling
try {
await apiClient.post('/api/v1/submissions/', { invalid: 'data' });
} catch (error) {
console.log('Caught error:', error.message);
console.log('User message:', formatErrorForUser(error));
}
```
---
## ✅ Task 1.4: Service Base Class (2 hours)
### Checklist
- [ ] Create `services/base/BaseService.ts`
- [ ] Add API client instance
- [ ] Implement common CRUD methods
- [ ] `get<T>(url: string): Promise<T>`
- [ ] `list<T>(url: string, params?: any): Promise<T[]>`
- [ ] `create<T>(url: string, data: any): Promise<T>`
- [ ] `update<T>(url: string, data: any): Promise<T>`
- [ ] `delete(url: string): Promise<void>`
- [ ] Add pagination helper methods
- [ ] `getPaginated<T>(url: string, params?: any): Promise<PaginatedResponse<T>>`
- [ ] Add filtering/sorting helpers
- [ ] Add error handling wrapper
- [ ] Add loading state management (optional)
- [ ] Document usage patterns
- [ ] Add TypeScript generics
### Acceptance Criteria
- [ ] Base service can be extended
- [ ] All CRUD operations work
- [ ] Pagination works correctly
- [ ] Errors are properly handled
- [ ] TypeScript types are inferred correctly
### Example Usage
```typescript
// Example service extending BaseService
class ParkService extends BaseService {
async getParks(params?: FilterParams) {
return this.getPaginated('/api/v1/parks/', params);
}
async getPark(id: string) {
return this.get(`/api/v1/parks/${id}/`);
}
}
```
---
## ✅ Task 1.5: Testing Infrastructure (3 hours)
### Checklist
- [ ] Install MSW: `bun add -d msw @testing-library/react @testing-library/jest-dom vitest`
- [ ] Create `services/__tests__/setup.ts`
- [ ] Initialize MSW
- [ ] Configure test environment
- [ ] Create `src/services/__tests__/mocks/handlers.ts`
- [ ] Mock Django API responses
- [ ] Create realistic test data
- [ ] Create `services/__tests__/mocks/server.ts`
- [ ] Set up MSW server
- [ ] Configure for Node/Next.js environment
- [ ] Create `services/__tests__/utils.ts`
- [ ] Test helper functions
- [ ] Mock data factories
- [ ] Write example service tests
- [ ] Test success cases
- [ ] Test error cases
- [ ] Test pagination
- [ ] Document testing patterns
- [ ] Set up CI test runs (if applicable)
### Acceptance Criteria
- [ ] MSW intercepts API calls in tests
- [ ] Tests can run without real API
- [ ] Test data is realistic
- [ ] All test utilities are documented
- [ ] CI passes tests
### Example Test
```typescript
import { describe, it, expect } from 'vitest';
import { ParkService } from '../parks/parkService';
describe('ParkService', () => {
it('should fetch parks', async () => {
const service = new ParkService();
const parks = await service.getParks();
expect(parks).toHaveLength(10);
});
});
```
---
## 🎯 Phase Completion Criteria
### Code Quality
- [ ] All files created and in correct locations
- [ ] Zero TypeScript errors
- [ ] Zero linter warnings
- [ ] Code is well-documented
- [ ] Naming conventions followed
### Functionality
- [ ] Can make requests to Django API
- [ ] Authentication works
- [ ] Error handling works
- [ ] Base service can be extended
- [ ] Tests pass
### Testing
- [ ] Unit tests for API client
- [ ] Unit tests for error handler
- [ ] Unit tests for base service
- [ ] Test coverage >80%
- [ ] All tests pass
---
## 📊 Progress Tracking
**Started:** [Date]
**Completed:** [Date]
**Time Spent:** [Hours]
### Tasks Completed
- [x] Task 1.0: Next.js 15 + Bun Setup
- [x] Task 1.1: Base API Client
- [ ] Task 1.2: Type Definitions - **DEFERRED** to be created as needed in Phases 2-11
- [x] Task 1.3: Error Handler Integration
- [ ] Task 1.4: Service Base Class - **DEFERRED** to Phase 4 (Entity Services)
- [ ] Task 1.5: Testing Infrastructure - **DEFERRED** to later phases
---
## 🚨 Blockers & Issues
Document any issues encountered:
1. **Issue:** [Description]
- **Impact:** [How it blocks progress]
- **Resolution:** [How it was resolved]
---
## 📝 Notes
Document important decisions and learnings:
- [Date] - [Note about implementation decision]
---
## ⏭️ Next Phase
Once this phase is complete, proceed to [Phase 2: Authentication](./PHASE_02_AUTHENTICATION.md)
---
## 🔗 Related Documentation
- [Environment Variables Guide](./ENVIRONMENT_VARIABLES.md)
- [Bun Setup Guide](./BUN_GUIDE.md)
- [Next.js 15 Migration Guide](./NEXTJS_15_MIGRATION_GUIDE.md)
- Next.js Documentation: https://nextjs.org/docs
- Bun Documentation: https://bun.sh/docs
---
**Document Version:** 2.0
**Last Updated:** November 9, 2025
**Changes:** Added Next.js 15 + App Router + Turbopack + Bun setup

View File

@@ -0,0 +1,336 @@
# PHASE 2: Authentication
**Status:** ⬜ Not Started
**Estimated Time:** 18-22 hours
**Priority:** CRITICAL
**Depends On:** Phase 1 (Foundation)
**Blocks:** Phases 3-13 (Almost everything requires auth)
---
## 🎯 Phase Goal
Replace ALL `supabase.auth.*` calls with Django authentication service, including JWT token management, OAuth, MFA, and session handling.
---
## 📋 Prerequisites
- [x] Phase 1 complete (API client, types, base service)
- [ ] Django auth endpoints tested and working
- [ ] JWT token format documented
- [ ] OAuth providers configured in Django
---
## 🗂️ Files to Create/Modify
```
src/services/auth/
├── authService.ts (NEW)
├── oauthService.ts (NEW)
├── mfaService.ts (NEW)
├── types.ts (NEW)
└── index.ts (NEW)
src/hooks/
├── useAuth.tsx (MODIFY - major rewrite)
src/components/auth/
├── AuthModal.tsx (MODIFY)
├── LoginForm.tsx (MODIFY)
├── RegisterForm.tsx (MODIFY)
├── PasswordResetForm.tsx (MODIFY)
├── TOTPSetup.tsx (MODIFY)
├── MFAChallenge.tsx (MODIFY)
└── MFARemovalDialog.tsx (MODIFY)
```
---
## ✅ Task 2.1: Auth Service Core (6 hours)
### Checklist
- [ ] Create `src/services/auth/authService.ts`
- [ ] Implement login
- [ ] `login(email: string, password: string): Promise<AuthResponse>`
- [ ] Store JWT token in localStorage
- [ ] Store refresh token
- [ ] Return user object
- [ ] Implement register
- [ ] `register(data: RegisterData): Promise<AuthResponse>`
- [ ] Handle email verification requirement
- [ ] Store tokens on success
- [ ] Implement logout
- [ ] `logout(): Promise<void>`
- [ ] Clear tokens from localStorage
- [ ] Invalidate on server
- [ ] Implement token refresh
- [ ] `refreshToken(): Promise<AuthResponse>`
- [ ] Auto-refresh on 401 errors
- [ ] Handle refresh token expiry
- [ ] Implement getCurrentUser
- [ ] `getCurrentUser(): Promise<User>`
- [ ] Use JWT token
- [ ] Cache user data
- [ ] Implement password reset flow
- [ ] `requestPasswordReset(email: string): Promise<void>`
- [ ] `confirmPasswordReset(token: string, password: string): Promise<void>`
- [ ] Implement email verification
- [ ] `verifyEmail(token: string): Promise<void>`
- [ ] `resendVerification(): Promise<void>`
- [ ] Implement email change
- [ ] `requestEmailChange(newEmail: string): Promise<void>`
- [ ] `confirmEmailChange(token: string): Promise<void>`
- [ ] Implement password change
- [ ] `changePassword(current: string, newPassword: string): Promise<void>`
### Django Endpoints to Use
```typescript
// Auth endpoints
POST /api/v1/auth/login // Email/password login
POST /api/v1/auth/register // User registration
POST /api/v1/auth/logout // Logout
POST /api/v1/auth/refresh // Refresh JWT
GET /api/v1/auth/me // Get current user
POST /api/v1/auth/password-reset // Request reset
POST /api/v1/auth/password-reset/confirm // Confirm reset
POST /api/v1/auth/verify-email // Verify email
POST /api/v1/auth/change-email // Change email
POST /api/v1/auth/change-password // Change password
```
### Acceptance Criteria
- [ ] Can login with email/password
- [ ] Tokens stored securely
- [ ] Auto-refresh works
- [ ] Logout clears all tokens
- [ ] Password reset flow works end-to-end
- [ ] Email verification works
- [ ] No Supabase dependencies
---
## ✅ Task 2.2: OAuth Integration (4 hours)
### Checklist
- [ ] Create `src/services/auth/oauthService.ts`
- [ ] Implement OAuth initiation
- [ ] `initiateOAuth(provider: 'google' | 'github'): void`
- [ ] Redirect to Django OAuth endpoint
- [ ] Handle state parameter for security
- [ ] Implement OAuth callback
- [ ] `handleOAuthCallback(code: string, state: string): Promise<AuthResponse>`
- [ ] Exchange code for tokens
- [ ] Store tokens
- [ ] Return user object
- [ ] Handle OAuth errors
- [ ] Access denied
- [ ] Invalid state
- [ ] Server errors
- [ ] Test with Google OAuth
- [ ] Test with GitHub OAuth
### Django Endpoints
```typescript
GET /api/v1/auth/oauth/{provider}/ // Initiate OAuth
GET /api/v1/auth/oauth/{provider}/callback // Handle callback
```
### Acceptance Criteria
- [ ] Google OAuth works
- [ ] GitHub OAuth works
- [ ] Errors handled gracefully
- [ ] State parameter validated
- [ ] Tokens stored correctly
---
## ✅ Task 2.3: MFA Service (3 hours)
### Checklist
- [ ] Create `src/services/auth/mfaService.ts`
- [ ] Implement TOTP setup
- [ ] `setupTOTP(): Promise<{ secret: string, qrCode: string }>`
- [ ] Generate QR code URL
- [ ] Return backup codes
- [ ] Implement TOTP verification
- [ ] `verifyTOTP(code: string): Promise<void>`
- [ ] Enable MFA after verification
- [ ] Implement MFA challenge
- [ ] `challengeMFA(code: string): Promise<AuthResponse>`
- [ ] Complete login with MFA code
- [ ] Implement MFA removal
- [ ] `removeMFA(password: string): Promise<void>`
- [ ] Require password confirmation
- [ ] Implement backup codes
- [ ] `generateBackupCodes(): Promise<string[]>`
- [ ] `useBackupCode(code: string): Promise<AuthResponse>`
### Django Endpoints
```typescript
POST /api/v1/auth/mfa/setup // Setup TOTP
POST /api/v1/auth/mfa/verify // Verify TOTP
POST /api/v1/auth/mfa/challenge // MFA challenge
DELETE /api/v1/auth/mfa/remove // Remove MFA
POST /api/v1/auth/mfa/backup-codes // Generate backup codes
```
### Acceptance Criteria
- [ ] TOTP setup works
- [ ] QR code displays correctly
- [ ] MFA challenge works during login
- [ ] Backup codes work
- [ ] MFA can be removed
---
## ✅ Task 2.4: Update useAuth Hook (5 hours)
### Checklist
- [ ] Open `src/hooks/useAuth.tsx`
- [ ] Remove ALL Supabase imports
- [ ] Replace `supabase.auth.onAuthStateChange` with custom implementation
- [ ] Check token validity on mount
- [ ] Set up interval to check token expiry
- [ ] Trigger refresh before expiry
- [ ] Replace `supabase.auth.getSession` with JWT validation
- [ ] Decode JWT to get user info
- [ ] Check expiry
- [ ] Auto-refresh if needed
- [ ] Update auth context state management
- [ ] `user` state
- [ ] `session` state
- [ ] `loading` state
- [ ] `initialized` state
- [ ] Implement token refresh logic
- [ ] Auto-refresh 5 minutes before expiry
- [ ] Handle refresh failures
- [ ] Logout on refresh failure
- [ ] Update all auth methods to use new service
- [ ] `signIn()`
- [ ] `signUp()`
- [ ] `signOut()`
- [ ] `resetPassword()`
- [ ] Handle session expiration gracefully
- [ ] Add error handling for all auth operations
- [ ] Test all auth flows
### Acceptance Criteria
- [ ] Hook works without Supabase
- [ ] Token refresh is automatic
- [ ] Session expiry is handled
- [ ] User state updates correctly
- [ ] Loading states work
- [ ] All error cases handled
---
## ✅ Task 2.5: Update Auth Components (4 hours)
### Checklist
- [ ] Update `AuthModal.tsx`
- [ ] Remove Supabase imports
- [ ] Use auth service instead
- [ ] Update error handling
- [ ] Update `LoginForm.tsx`
- [ ] Use `authService.login()`
- [ ] Handle MFA challenge
- [ ] Show appropriate errors
- [ ] Update `RegisterForm.tsx`
- [ ] Use `authService.register()`
- [ ] Handle validation errors
- [ ] Show email verification message
- [ ] Update `PasswordResetForm.tsx`
- [ ] Use `authService.requestPasswordReset()`
- [ ] Use `authService.confirmPasswordReset()`
- [ ] Handle token expiry
- [ ] Update `TOTPSetup.tsx`
- [ ] Use `mfaService.setupTOTP()`
- [ ] Display QR code
- [ ] Handle verification
- [ ] Update `MFAChallenge.tsx`
- [ ] Use `mfaService.challengeMFA()`
- [ ] Handle backup codes
- [ ] Update `MFARemovalDialog.tsx`
- [ ] Use `mfaService.removeMFA()`
- [ ] Require password confirmation
- [ ] Remove all `supabase.*` imports
- [ ] Test all UI flows
- [ ] Update error messages to be user-friendly
### Acceptance Criteria
- [ ] All forms work correctly
- [ ] Errors display properly
- [ ] Loading states work
- [ ] Success messages show
- [ ] No Supabase references remain
---
## 🎯 Phase Completion Criteria
### Code Quality
- [ ] Zero `supabase.auth.*` calls in codebase
- [ ] All TypeScript errors resolved
- [ ] No linter warnings
- [ ] Code well-documented
- [ ] Consistent error handling
### Functionality
- [ ] Login works (email/password)
- [ ] Registration works
- [ ] Logout works
- [ ] Password reset works
- [ ] Email verification works
- [ ] Google OAuth works
- [ ] GitHub OAuth works
- [ ] TOTP MFA works
- [ ] Backup codes work
- [ ] Token refresh is automatic
- [ ] Session expiry handled
### Testing
- [ ] Can login as regular user
- [ ] Can login with OAuth
- [ ] Can login with MFA
- [ ] Can reset password
- [ ] Can change email
- [ ] Can change password
- [ ] Token refresh works automatically
- [ ] Session expiry redirects to login
- [ ] All error cases handled
---
## 📊 Progress Tracking
**Started:** [Date]
**Completed:** [Date]
**Time Spent:** [Hours]
### Tasks Completed
- [ ] Task 2.1: Auth Service Core
- [ ] Task 2.2: OAuth Integration
- [ ] Task 2.3: MFA Service
- [ ] Task 2.4: Update useAuth Hook
- [ ] Task 2.5: Update Auth Components
---
## 🚨 Blockers & Issues
Document any issues:
1. **Issue:** [Description]
- **Impact:** [Impact]
- **Resolution:** [Resolution]
---
## 📝 Notes
- [Date] - [Important decision or learning]
---
## ⏭️ Next Phase
Once complete, proceed to [Phase 3: Sacred Pipeline](./PHASE_03_SACRED_PIPELINE.md)

View File

@@ -0,0 +1,369 @@
# PHASE 3: Sacred Pipeline - Submissions
**Status:** ⬜ Not Started
**Estimated Time:** 20-25 hours
**Priority:** ⚠️ CRITICAL - THIS IS THE HEART OF THRILLWIKI
**Depends On:** Phase 1 (Foundation), Phase 2 (Authentication)
**Blocks:** All entity creation/editing features
---
## 🎯 Phase Goal
Replace the ENTIRE submission system from Supabase to Django while maintaining 100% Sacred Pipeline integrity: **Form → Submission → Moderation → Approval → Versioning → Display**
---
## ⚠️ CRITICAL WARNING
The Sacred Pipeline is NON-NEGOTIABLE. Every single entity creation/edit MUST flow through:
1. User submits form
2. Creates submission record
3. Goes to moderation queue
4. Moderator approves/rejects
5. On approval → creates/updates entity + version history
6. Displays on site
**NEVER bypass moderation!** Exception: Users with moderator+ roles skip queue.
---
## 📋 Prerequisites
- [x] Phase 1 complete (API client, types, base service)
- [x] Phase 2 complete (Authentication working)
- [ ] Django submission endpoints tested
- [ ] Understanding of `entitySubmissionHelpers.ts` (1,200+ lines!)
- [ ] Django Sacred Pipeline working and tested
---
## 🗂️ Files to Create/Modify
```
src/services/submissions/
├── submissionService.ts (NEW)
├── types.ts (NEW)
└── index.ts (NEW)
src/lib/
├── entitySubmissionHelpers.ts (MAJOR REWRITE - 1,200+ lines!)
Forms that need updating:
src/components/forms/
├── ParkForm.tsx (MODIFY)
├── RideForm.tsx (MODIFY)
├── CompanyForm.tsx (MODIFY)
├── RideModelForm.tsx (MODIFY)
└── TimelineEventForm.tsx (MODIFY)
```
---
## ✅ Task 3.1: Submission Service Core (8 hours)
### Checklist
- [ ] Create `src/services/submissions/submissionService.ts`
- [ ] Implement create submission
- [ ] `createSubmission(data: SubmissionData): Promise<Submission>`
- [ ] Handle single entity submissions
- [ ] Handle composite submissions (park + operator)
- [ ] Map form data to Django schema
- [ ] Implement create with items
- [ ] `createSubmissionWithItems(items: SubmissionItem[]): Promise<Submission>`
- [ ] Handle dependency order
- [ ] Handle temp IDs for relationships
- [ ] Implement get submission
- [ ] `getSubmission(id: string): Promise<Submission>`
- [ ] Include all submission items
- [ ] Include relational data
- [ ] Implement list submissions
- [ ] `listSubmissions(filters?: SubmissionFilters): Promise<Submission[]>`
- [ ] Filter by user
- [ ] Filter by status
- [ ] Filter by type
- [ ] Implement update submission status
- [ ] For moderators only
- [ ] Handle in moderation service
- [ ] Handle errors properly
- [ ] Validation errors
- [ ] Rate limiting
- [ ] Network errors
- [ ] Add retry logic
- [ ] Test with simple submission
- [ ] Test with composite submission
### Django Endpoints
```typescript
POST /api/v1/submissions/ // Create submission
POST /api/v1/submissions/with-items/ // Create with items
GET /api/v1/submissions/{id}/ // Get submission
GET /api/v1/submissions/ // List submissions
GET /api/v1/submissions/my/ // User's submissions
```
### Acceptance Criteria
- [ ] Can create park submission
- [ ] Can create ride submission
- [ ] Can create composite (park + operator)
- [ ] Submission goes to moderation queue
- [ ] Errors are handled correctly
- [ ] Rate limiting works
---
## ✅ Task 3.2: Rewrite entitySubmissionHelpers.ts (12 hours)
### ⚠️ THIS IS THE BIG ONE
This file has 1,200+ lines of Supabase code. Every function must be rewritten to use Django service.
### Functions to Rewrite
#### Park Submissions
- [ ] `submitParkCreation()` - Create new park
- [ ] Use submission service
- [ ] Handle location data
- [ ] Handle operator/owner references
- [ ] Handle images
- [ ] Test simple park creation
- [ ] Test park + operator creation
- [ ] Test park + operator + owner creation
- [ ] `submitParkUpdate()` - Edit existing park
- [ ] Fetch existing park data
- [ ] Extract changed fields only
- [ ] Block new photo uploads (use gallery)
- [ ] Allow banner/card reassignments
- [ ] Test park edit
#### Ride Submissions
- [ ] `submitRideCreation()` - Create new ride
- [ ] Handle park reference
- [ ] Handle manufacturer/designer references
- [ ] Handle ride model reference
- [ ] Handle images
- [ ] Handle technical specifications
- [ ] Test simple ride creation
- [ ] Test ride + manufacturer creation
- [ ] Test ride + park + manufacturer creation
- [ ] `submitRideUpdate()` - Edit existing ride
- [ ] Fetch existing ride data
- [ ] Extract changed fields
- [ ] Block new photo uploads
- [ ] Test ride edit
#### Company Submissions
- [ ] `submitManufacturerCreation()` - Create manufacturer
- [ ] `submitManufacturerUpdate()` - Edit manufacturer
- [ ] `submitDesignerCreation()` - Create designer
- [ ] `submitDesignerUpdate()` - Edit designer
- [ ] `submitOperatorCreation()` - Create operator
- [ ] `submitOperatorUpdate()` - Edit operator
- [ ] `submitPropertyOwnerCreation()` - Create property owner
- [ ] `submitPropertyOwnerUpdate()` - Edit property owner
#### Ride Model Submissions
- [ ] `submitRideModelCreation()` - Create ride model
- [ ] Handle manufacturer reference
- [ ] Handle technical specifications
- [ ] Test ride model creation
- [ ] `submitRideModelUpdate()` - Edit ride model
- [ ] Test ride model edit
#### Timeline Event Submissions
- [ ] `submitTimelineEvent()` - Create timeline event
- [ ] Handle entity references
- [ ] Handle date precision
- [ ] Test timeline creation
- [ ] `submitTimelineEventUpdate()` - Edit timeline event
- [ ] Test timeline edit
#### Composite Submissions
- [ ] `submitCompositeCreation()` - Master function
- [ ] Handle dependency ordering
- [ ] Map temp IDs to order indices
- [ ] Validate all temp refs
- [ ] Handle all entity combinations:
- [ ] Park + Operator
- [ ] Park + Operator + Owner
- [ ] Ride + Manufacturer
- [ ] Ride + Manufacturer + Model
- [ ] Ride + Park + Manufacturer + Model
- [ ] Test all combinations
### CRITICAL REQUIREMENTS
1. **NO JSON in SQL** - Use relational tables:
- `park_submissions`
- `ride_submissions`
- `company_submissions`
- `ride_model_submissions`
- `timeline_event_submissions`
2. **Rate Limiting** - Keep existing logic:
- `checkRateLimitOrThrow()`
- `recordSubmissionAttempt()`
3. **Ban Checking** - Keep existing logic:
- Check if user is banned
- Report ban evasion attempts
4. **Image Handling**:
- Upload images first
- Only allow uploads on creation
- Block uploads on edits (use gallery)
- Allow banner/card reassignments
5. **Error Handling**:
- Retry logic with exponential backoff
- Validation errors don't retry
- Network errors do retry (max 3)
- User-friendly error messages
6. **Validation**:
- Client-side validation before submit
- Use validation functions
- Clear error messages
### Testing Checklist
- [ ] Create park (simple)
- [ ] Create park + operator
- [ ] Create park + operator + owner (same company)
- [ ] Create park + operator + owner (different companies)
- [ ] Edit park
- [ ] Create ride (simple)
- [ ] Create ride + manufacturer
- [ ] Create ride + manufacturer + model
- [ ] Create ride + park + manufacturer + model
- [ ] Edit ride
- [ ] Create manufacturer
- [ ] Edit manufacturer
- [ ] Create designer
- [ ] Edit designer
- [ ] Create operator
- [ ] Edit operator
- [ ] Create property owner
- [ ] Edit property owner
- [ ] Create ride model
- [ ] Edit ride model
- [ ] Create timeline event
- [ ] Edit timeline event
- [ ] Rate limiting works
- [ ] Ban checking works
- [ ] Validation works
- [ ] Error handling works
- [ ] Retry logic works
- [ ] All go to moderation queue
- [ ] Moderators skip queue
### Acceptance Criteria
- [ ] Zero `supabase.*` calls in file
- [ ] All 16+ submission types work
- [ ] Composite submissions work
- [ ] Sacred Pipeline intact
- [ ] Rate limiting works
- [ ] Ban checking works
- [ ] Images upload correctly
- [ ] Validation prevents bad data
- [ ] Errors are user-friendly
- [ ] All submissions go to moderation
- [ ] TypeScript compiles without errors
---
## 🎯 Phase Completion Criteria
### Code Quality
- [ ] Zero Supabase calls in `entitySubmissionHelpers.ts`
- [ ] Zero Supabase calls in form components
- [ ] All TypeScript errors resolved
- [ ] No linter warnings
- [ ] Code well-documented
- [ ] Consistent error handling
### Functionality - MUST TEST ALL
- [ ] Park creation works
- [ ] Park editing works
- [ ] Park + operator creation works
- [ ] Ride creation works
- [ ] Ride editing works
- [ ] Ride + manufacturer creation works
- [ ] Ride + manufacturer + model creation works
- [ ] Manufacturer creation/editing works
- [ ] Designer creation/editing works
- [ ] Operator creation/editing works
- [ ] Property owner creation/editing works
- [ ] Ride model creation/editing works
- [ ] Timeline event creation/editing works
- [ ] Composite submissions work
- [ ] All submissions go to moderation queue
- [ ] Moderators can skip queue
- [ ] Rate limiting prevents spam
- [ ] Ban checking prevents abuse
- [ ] Images upload correctly
- [ ] Validation catches errors
- [ ] Error messages are clear
### Sacred Pipeline Integrity
- [ ] ✅ Form captured correctly
- [ ] ✅ Submission created in Django
- [ ] ✅ Goes to moderation queue
- [ ] ✅ Moderator can approve/reject
- [ ] ✅ Approval creates entity + version
- [ ] ✅ Entity displays on site
- [ ] ✅ Version history recorded
- [ ] ✅ NEVER bypasses moderation (except moderators)
---
## 📊 Progress Tracking
**Started:** [Date]
**Completed:** [Date]
**Time Spent:** [Hours]
### Tasks Completed
- [ ] Task 3.1: Submission Service Core
- [ ] Task 3.2: Rewrite entitySubmissionHelpers.ts
- [ ] Park submissions
- [ ] Ride submissions
- [ ] Company submissions
- [ ] Ride model submissions
- [ ] Timeline event submissions
- [ ] Composite submissions
- [ ] Testing all submission types
---
## 🚨 Blockers & Issues
Document any issues:
1. **Issue:** [Description]
- **Impact:** [Impact]
- **Resolution:** [Resolution]
---
## 📝 Notes
**Important Decisions:**
- [Date] - [Decision about submission flow]
**Learnings:**
- [Date] - [Important learning]
---
## ⏭️ Next Phase
Once complete, proceed to [Phase 4: Entity Services](./PHASE_04_ENTITY_SERVICES.md)
---
## 🆘 If You Get Stuck
1. Check Django submission endpoints work: `django/api/v1/endpoints/submissions.py`
2. Check Django submission services: `django/apps/entities/services/`
3. Review Sacred Pipeline docs: `django/SACRED_PIPELINE_AUDIT_AND_IMPLEMENTATION_PLAN.md`
4. Test with Postman/curl first
5. Don't proceed until submissions work end-to-end

View File

@@ -0,0 +1,57 @@
# PHASE 4: Entity Services
**Status:** ⬜ Not Started
**Estimated Time:** 25-30 hours
**Priority:** HIGH
**Depends On:** Phase 1 (Foundation)
**Blocks:** Phase 12 (Pages Migration)
## 🎯 Goal
Create service layer for all entities (Parks, Rides, Companies, Ride Models) to replace Supabase queries.
## Tasks
### Task 4.1: Parks Service (5h)
- [ ] Create `src/services/entities/parksService.ts`
- [ ] List parks with filters
- [ ] Get park by ID/slug
- [ ] Get park rides
- [ ] Pagination support
- [ ] Update hooks: `useParks`, `useParkDetail`, `useParkRides`
### Task 4.2: Rides Service (5h)
- [ ] Create `src/services/entities/ridesService.ts`
- [ ] List rides with filters
- [ ] Get ride by ID/slug
- [ ] Get ride reviews/photos/credits
- [ ] Update hooks: `useRides`, `useRideDetail`
### Task 4.3: Companies Service (5h)
- [ ] Create `src/services/entities/companiesService.ts`
- [ ] List companies by type
- [ ] Get company by ID/slug
- [ ] Get company parks/rides/models
- [ ] Update company hooks
### Task 4.4: Ride Models Service (3h)
- [ ] Create `src/services/entities/rideModelsService.ts`
- [ ] List ride models
- [ ] Get model by ID/slug
- [ ] Get model installations
### Task 4.5: Update All Entity Hooks (7h)
- [ ] Replace all Supabase calls
- [ ] Test caching behavior
- [ ] Verify loading states
## Completion Criteria
- [ ] All entity queries use services
- [ ] No `supabase.from()` calls remain
- [ ] All hooks work correctly
- [ ] Pagination works
- [ ] Filtering works
---
## ⏭️ Next Phase
Once complete, proceed to [Phase 5: Reviews & Social](./PHASE_05_REVIEWS_SOCIAL.md)

View File

@@ -0,0 +1,287 @@
# PHASE 5: Reviews & Social Features
**Status:** ⬜ Not Started
**Estimated Time:** 12-15 hours
**Priority:** HIGH
**Depends On:** Phase 1 (Foundation), Phase 4 (Entity Services)
**Blocks:** Phase 12 (Pages Migration)
---
## 🎯 Goal
Build service layer for reviews, ride credits, and top lists to replace ALL Supabase queries related to social features.
---
## 📋 Tasks
### Task 5.1: Reviews Service (5 hours)
**File:** `src/services/reviews/reviewsService.ts`
#### Create Core Service
```typescript
class ReviewsService extends BaseService {
// GET /reviews/
async getReviews(filters: ReviewFilters): Promise<PaginatedResponse<Review>>
// GET /reviews/{id}/
async getReview(id: string): Promise<Review>
// POST /reviews/ (creates submission)
async submitReview(data: ReviewSubmissionData): Promise<ContentSubmission>
// PATCH /reviews/{id}/ (creates submission)
async updateReview(id: string, data: Partial<ReviewData>): Promise<ContentSubmission>
// DELETE /reviews/{id}/ (creates submission)
async deleteReview(id: string, reason: string): Promise<ContentSubmission>
// POST /reviews/{id}/helpful/
async markHelpful(reviewId: string): Promise<void>
// DELETE /reviews/{id}/helpful/
async unmarkHelpful(reviewId: string): Promise<void>
// GET /reviews/?entity_type=ride&entity_id={id}
async getEntityReviews(entityType: string, entityId: string): Promise<Review[]>
// GET /reviews/?user_id={id}
async getUserReviews(userId: string): Promise<Review[]>
}
```
#### Checklist
- [ ] Create `reviewsService.ts` with all methods
- [ ] Create `types.ts` for Review types
- [ ] Create `mappers.ts` for API ↔ Frontend transformations
- [ ] Add error handling for all endpoints
- [ ] Implement proper TypeScript types
- [ ] Export from `src/services/reviews/index.ts`
---
### Task 5.2: Ride Credits Service (3 hours)
**File:** `src/services/rideCredits/rideCreditsService.ts`
#### Create Core Service
```typescript
class RideCreditsService extends BaseService {
// GET /ride-credits/
async getRideCredits(userId: string): Promise<UserRideCredit[]>
// GET /ride-credits/{id}/
async getRideCredit(id: string): Promise<UserRideCredit>
// POST /ride-credits/ (creates submission)
async submitRideCredit(data: RideCreditData): Promise<ContentSubmission>
// PATCH /ride-credits/{id}/ (creates submission)
async updateRideCredit(id: string, data: Partial<RideCreditData>): Promise<ContentSubmission>
// DELETE /ride-credits/{id}/ (creates submission)
async deleteRideCredit(id: string): Promise<ContentSubmission>
// GET /ride-credits/?ride_id={id}
async getRideRideCredits(rideId: string): Promise<UserRideCredit[]>
}
```
#### Checklist
- [ ] Create `rideCreditsService.ts` with all methods
- [ ] Create types for ride credits
- [ ] Create mappers for API transformations
- [ ] Handle submission creation properly
- [ ] Export from `src/services/rideCredits/index.ts`
---
### Task 5.3: Top Lists Service (4 hours)
**File:** `src/services/topLists/topListsService.ts`
#### Create Core Service
```typescript
class TopListsService extends BaseService {
// GET /top-lists/
async getTopLists(filters?: TopListFilters): Promise<UserTopList[]>
// GET /top-lists/{id}/
async getTopList(id: string): Promise<UserTopList>
// POST /top-lists/
async createTopList(data: TopListData): Promise<UserTopList>
// PATCH /top-lists/{id}/
async updateTopList(id: string, data: Partial<TopListData>): Promise<UserTopList>
// DELETE /top-lists/{id}/
async deleteTopList(id: string): Promise<void>
// GET /top-lists/{id}/items/
async getTopListItems(listId: string): Promise<UserTopListItem[]>
// POST /top-lists/{id}/items/
async addTopListItem(listId: string, item: TopListItemData): Promise<UserTopListItem>
// PATCH /top-lists/{id}/items/reorder/
async reorderTopListItems(listId: string, itemIds: string[]): Promise<void>
// DELETE /top-lists/{id}/items/{itemId}/
async removeTopListItem(listId: string, itemId: string): Promise<void>
// GET /top-lists/?user_id={id}
async getUserTopLists(userId: string): Promise<UserTopList[]>
}
```
#### Checklist
- [ ] Create `topListsService.ts` with all methods
- [ ] Create types for top lists and items
- [ ] Create mappers for API transformations
- [ ] Handle reordering logic
- [ ] Handle item CRUD operations
- [ ] Export from `src/services/topLists/index.ts`
---
### Task 5.4: Update Review Components (3 hours)
**Files to Update:**
- `src/components/reviews/ReviewForm.tsx`
- `src/components/reviews/ReviewsList.tsx`
- `src/components/reviews/ReviewCard.tsx`
- `src/components/reviews/HelpfulButton.tsx`
#### Checklist
- [ ] Replace `supabase.from('reviews')` with `reviewsService`
- [ ] Update review submission to use service
- [ ] Update helpful votes to use service
- [ ] Update review deletion to use service
- [ ] Test review CRUD operations
- [ ] Verify error handling works
---
### Task 5.5: Update Ride Credit Components (2 hours)
**Files to Update:**
- `src/components/profile/RideCreditsManager.tsx`
- `src/components/profile/AddRideCreditDialog.tsx`
- `src/components/profile/RideCreditCard.tsx`
#### Checklist
- [ ] Replace all Supabase calls with `rideCreditsService`
- [ ] Update submission flow to use service
- [ ] Test adding ride credits
- [ ] Test editing ride credits
- [ ] Test deleting ride credits
- [ ] Verify moderation queue receives submissions
---
### Task 5.6: Update Top Lists Components (3 hours)
**Files to Update:**
- `src/components/lists/UserListManager.tsx`
- `src/components/lists/ListDisplay.tsx`
- `src/components/lists/ListItemEditor.tsx`
- `src/components/lists/ListSearch.tsx`
#### Checklist
- [ ] Replace all Supabase calls with `topListsService`
- [ ] Update list CRUD operations
- [ ] Update item reordering logic
- [ ] Test creating/editing lists
- [ ] Test adding/removing/reordering items
- [ ] Verify list sharing works (if implemented)
---
## 🎯 Success Criteria
### Service Layer
- [ ] All three services (reviews, rideCredits, topLists) created
- [ ] All services extend BaseService
- [ ] All services have proper TypeScript types
- [ ] All services have error handling
- [ ] All services have mappers for API transformations
### Components
- [ ] Zero `supabase.from('reviews')` calls
- [ ] Zero `supabase.from('user_ride_credits')` calls
- [ ] Zero `supabase.from('user_top_lists')` calls
- [ ] Zero `supabase.from('user_top_list_items')` calls
- [ ] All review operations work
- [ ] All ride credit operations work
- [ ] All top list operations work
### Testing
- [ ] Can create a review (goes to moderation)
- [ ] Can mark review as helpful
- [ ] Can add a ride credit (goes to moderation)
- [ ] Can create a top list
- [ ] Can add items to top list
- [ ] Can reorder top list items
- [ ] Can delete top list items
- [ ] Can delete entire top list
### Sacred Pipeline
- [ ] Review submissions go through moderation
- [ ] Review updates go through moderation
- [ ] Review deletions go through moderation
- [ ] Ride credit submissions go through moderation
- [ ] Pipeline integrity maintained
---
## 📝 Implementation Notes
### Review Submissions
- Reviews are user-generated content and MUST go through the Sacred Pipeline
- Use `reviewsService.submitReview()` which creates a ContentSubmission
- Only approved reviews appear in the system
### Ride Credits
- Ride credits are also user-generated and MUST go through moderation
- Users claim they've ridden a ride, moderators verify
- Auto-crediting on review approval should still work
### Top Lists
- Top lists are personal user content
- May NOT require moderation (check with backend team)
- Item reordering is immediate (no moderation)
### Helpful Votes
- Marking a review as helpful is immediate (no moderation)
- Backend tracks vote counts automatically
- Each user can only vote once per review
---
## 🔗 Related Files
### Services
- `src/services/reviews/reviewsService.ts`
- `src/services/reviews/types.ts`
- `src/services/reviews/mappers.ts`
- `src/services/rideCredits/rideCreditsService.ts`
- `src/services/topLists/topListsService.ts`
### Components
- All files in `src/components/reviews/`
- All files in `src/components/profile/` (ride credits)
- All files in `src/components/lists/` (top lists)
### Hooks
- `src/hooks/useReviews.ts`
- `src/hooks/useRideCredits.ts`
- `src/hooks/useTopLists.ts`
---
## ⏭️ Next Phase
**Phase 6:** Moderation & Admin - Create moderation service and update moderation queue components.

View File

@@ -0,0 +1,256 @@
# PHASE 6: Moderation & Admin
**Status:** ⬜ Not Started
**Estimated Time:** 10-12 hours
**Priority:** CRITICAL
**Depends On:** Phase 1 (Foundation), Phase 3 (Sacred Pipeline)
**Blocks:** Phase 12 (Pages Migration)
---
## 🎯 Goal
Complete migration of moderation system from Supabase to Django, including moderation queue, approval/rejection flows, and admin dashboard.
---
## 📋 Tasks
### Task 6.1: Moderation Service (4 hours)
**File:** `src/services/moderation/moderationService.ts`
#### Create Core Service
```typescript
class ModerationService extends BaseService {
// GET /moderation/queue/
async getModerationQueue(filters?: QueueFilters): Promise<PaginatedResponse<ContentSubmission>>
// POST /moderation/queue/claim/
async claimNextSubmission(): Promise<ContentSubmission | null>
// POST /moderation/submissions/{id}/start-review/
async startReview(submissionId: string): Promise<ModerationLock>
// POST /moderation/submissions/{id}/extend-lock/
async extendLock(submissionId: string): Promise<ModerationLock>
// POST /moderation/submissions/{id}/release-lock/
async releaseLock(submissionId: string): Promise<void>
// POST /moderation/submissions/{id}/approve/
async approveSubmission(submissionId: string, notes?: string): Promise<void>
// POST /moderation/submissions/{id}/reject/
async rejectSubmission(submissionId: string, reason: string): Promise<void>
// POST /moderation/submissions/{id}/approve-selective/
async approveSelective(submissionId: string, itemIds: string[]): Promise<void>
// GET /moderation/stats/
async getModerationStats(): Promise<ModerationStats>
// GET /moderation/submissions/{id}/
async getSubmission(submissionId: string): Promise<ContentSubmission>
// GET /moderation/submissions/{id}/items/
async getSubmissionItems(submissionId: string): Promise<SubmissionItem[]>
}
```
#### Checklist
- [ ] Create `moderationService.ts` with all methods
- [ ] Create types for moderation operations
- [ ] Create mappers for API transformations
- [ ] Handle lock management properly
- [ ] Handle selective approval logic
- [ ] Export from `src/services/moderation/index.ts`
---
### Task 6.2: Update ModerationQueue Component (3 hours)
**File:** `src/components/moderation/ModerationQueue.tsx`
#### Replace ALL Supabase Calls
```typescript
// OLD - Supabase
const { data } = await supabase.from('content_submissions')
.select('*')
.eq('status', 'pending_review');
// NEW - Django Service
const submissions = await moderationService.getModerationQueue({
status: 'pending_review'
});
```
#### Checklist
- [ ] Replace queue fetching with `moderationService.getModerationQueue()`
- [ ] Replace claim logic with `moderationService.claimNextSubmission()`
- [ ] Update lock extension with `moderationService.extendLock()`
- [ ] Update lock release with `moderationService.releaseLock()`
- [ ] Remove all realtime subscriptions (Django uses polling or WebSockets)
- [ ] Test queue display
- [ ] Test claiming submissions
- [ ] Verify lock management works
---
### Task 6.3: Update Approval/Rejection Components (2 hours)
**Files:**
- `src/components/moderation/ApprovalDialog.tsx`
- `src/components/moderation/RejectionDialog.tsx`
- `src/components/moderation/SelectiveApprovalDialog.tsx`
#### Checklist
- [ ] Replace approval with `moderationService.approveSubmission()`
- [ ] Replace rejection with `moderationService.rejectSubmission()`
- [ ] Replace selective approval with `moderationService.approveSelective()`
- [ ] Update success/error handling
- [ ] Test all three approval flows
- [ ] Verify Sacred Pipeline integrity
---
### Task 6.4: Update Admin Dashboard (3 hours)
**Files:**
- `src/pages/AdminDashboard.tsx`
- `src/components/admin/ModerationStats.tsx`
- `src/components/admin/PipelineHealthAlerts.tsx`
#### Replace Admin Queries
```typescript
// OLD - Supabase
const { data } = await supabase.rpc('get_moderation_stats');
// NEW - Django Service
const stats = await moderationService.getModerationStats();
```
#### Checklist
- [ ] Replace `supabase.rpc('get_moderation_stats')` with service
- [ ] Update pipeline health monitoring
- [ ] Remove Supabase admin audit log calls
- [ ] Update admin user management (if any)
- [ ] Test admin dashboard loads
- [ ] Test stats display correctly
---
## 🎯 Success Criteria
### Service Layer
- [ ] ModerationService created and fully functional
- [ ] All moderation operations use service
- [ ] Lock management works correctly
- [ ] Selective approval works
- [ ] Stats fetching works
### Components
- [ ] Zero `supabase.from('content_submissions')` calls
- [ ] Zero `supabase.from('moderation_locks')` calls
- [ ] Zero `supabase.rpc()` calls for moderation
- [ ] ModerationQueue works with Django
- [ ] All approval flows work
- [ ] Admin dashboard works
### Testing
- [ ] Can view moderation queue
- [ ] Can claim a submission
- [ ] Lock extends automatically
- [ ] Can approve submission
- [ ] Can reject submission
- [ ] Can selective approve (approve some items, reject others)
- [ ] Lock releases on page navigation
- [ ] Stats update correctly
- [ ] Pipeline health monitoring works
### Sacred Pipeline
- [ ] All approvals go through Django
- [ ] No bypass of moderation system
- [ ] Versioning happens on approval
- [ ] Rejection reasons are saved
- [ ] Admin notes are saved
---
## 📝 Implementation Notes
### Lock Management
- Locks prevent multiple moderators from reviewing same submission
- Locks auto-extend every 30 seconds (client-side timer)
- Locks auto-release after 5 minutes of inactivity (server-side)
- Must release lock on component unmount
- Must release lock on page navigation
### Selective Approval
- Allows approving some submission items while rejecting others
- Common for batch submissions (e.g., adding 10 rides at once)
- Each item can be individually approved/rejected
- Must maintain data integrity
### Realtime Updates
- Supabase used realtime subscriptions for queue updates
- Django migration: either polling (every 10s) or WebSockets
- For MVP, polling is acceptable
- Queue should refresh when lock is released
### Admin Permissions
- Only users with `role='moderator'` or `role='admin'` can access
- Backend enforces permissions on all endpoints
- Frontend should hide moderation UI for non-moderators
---
## 🔗 Related Files
### Services
- `src/services/moderation/moderationService.ts`
- `src/services/moderation/types.ts`
- `src/services/moderation/mappers.ts`
### Components
- `src/components/moderation/ModerationQueue.tsx`
- `src/components/moderation/QueueFilters.tsx`
- `src/components/moderation/SubmissionCard.tsx`
- `src/components/moderation/ApprovalDialog.tsx`
- `src/components/moderation/RejectionDialog.tsx`
- `src/components/moderation/SelectiveApprovalDialog.tsx`
- `src/components/admin/ModerationStats.tsx`
- `src/components/admin/PipelineHealthAlerts.tsx`
### Pages
- `src/pages/AdminDashboard.tsx`
- `src/pages/ModerationQueue.tsx`
### Hooks
- `src/hooks/useModerationQueue.ts`
- `src/hooks/useModerationStats.ts`
- `src/hooks/useModerationLock.ts`
---
## 🚨 Critical Warnings
### DO NOT
- ❌ Create any bypass mechanism for moderation
- ❌ Allow direct database writes without submissions
- ❌ Skip versioning on approval
- ❌ Allow approvals without proper permissions
- ❌ Leave locks hanging (always cleanup)
### MUST DO
- ✅ ALL entity changes go through ContentSubmission
- ✅ Locks are properly managed and released
- ✅ Selective approval maintains data integrity
- ✅ Admin permissions are enforced
- ✅ Rejection reasons are mandatory
---
## ⏭️ Next Phase
**Phase 7:** Media & Photos - Migrate photo upload and management to Django + CloudFlare Images.

View File

@@ -0,0 +1,121 @@
# PHASE 7: Media & Photos
**Status:** ⬜ Not Started
**Estimated Time:** 8-10 hours
**Priority:** HIGH
**Depends On:** Phase 1 (Foundation)
**Blocks:** Phase 12 (Pages Migration)
---
## 🎯 Goal
Migrate photo upload and management from Supabase Storage to Django + CloudFlare Images.
---
## 📋 Tasks
### Task 7.1: Photos Service (3 hours)
**File:** `src/services/photos/photosService.ts`
```typescript
class PhotosService extends BaseService {
// GET /photos/
async getPhotos(filters?: PhotoFilters): Promise<PaginatedResponse<Photo>>
// GET /photos/{id}/
async getPhoto(id: string): Promise<Photo>
// POST /photos/upload/
async uploadPhoto(file: File, metadata: PhotoMetadata): Promise<Photo>
// PATCH /photos/{id}/
async updatePhoto(id: string, data: Partial<PhotoData>): Promise<Photo>
// DELETE /photos/{id}/
async deletePhoto(id: string): Promise<void>
// GET /photos/?entity_type={type}&entity_id={id}
async getEntityPhotos(entityType: string, entityId: string): Promise<Photo[]>
// POST /photos/cloudflare-upload-url/
async getUploadUrl(): Promise<{ uploadUrl: string; imageId: string }>
}
```
#### Checklist
- [ ] Create `photosService.ts`
- [ ] Handle CloudFlare upload workflow
- [ ] Create types and mappers
- [ ] Handle photo metadata properly
---
### Task 7.2: Update Photo Upload Components (3 hours)
**Files:**
- `src/components/upload/PhotoUpload.tsx`
- `src/components/upload/UppyPhotoUpload.tsx`
- `src/components/upload/EntityMultiImageUploader.tsx`
#### New Upload Flow
1. Get CloudFlare upload URL from Django
2. Upload file directly to CloudFlare
3. Send CloudFlare image ID + metadata to Django
4. Django creates Photo record
#### Checklist
- [ ] Replace Supabase Storage with CloudFlare
- [ ] Update upload workflow
- [ ] Handle upload errors
- [ ] Test single photo upload
- [ ] Test batch photo uploads
---
### Task 7.3: Update Photo Management (2 hours)
**Files:**
- `src/components/upload/PhotoManagementDialog.tsx`
- `src/components/upload/EntityPhotoGallery.tsx`
#### Checklist
- [ ] Replace photo queries with `photosService`
- [ ] Update caption editing
- [ ] Update photo deletion
- [ ] Test photo gallery display
- [ ] Test photo management operations
---
### Task 7.4: Update Photo Display Components (2 hours)
**Files:**
- `src/components/rides/RecentPhotosPreview.tsx`
- Various entity detail pages with photo galleries
#### Checklist
- [ ] Replace all Supabase Storage URLs with CloudFlare URLs
- [ ] Update image optimization parameters
- [ ] Test responsive images
- [ ] Verify lazy loading works
---
## 🎯 Success Criteria
- [ ] Zero `supabase.storage` calls
- [ ] All photo uploads go through CloudFlare
- [ ] Photo metadata saved to Django
- [ ] Photo galleries work
- [ ] Photo captions can be updated
- [ ] Photos can be deleted
- [ ] Image URLs are CloudFlare URLs
---
## ⏭️ Next Phase
**Phase 8:** Search & Discovery

View File

@@ -0,0 +1,197 @@
# PHASE 8: Search & Discovery
**Status:** ⬜ Not Started
**Estimated Time:** 8-10 hours
**Priority:** HIGH
**Depends On:** Phase 1 (Foundation), Phase 4 (Entity Services)
**Blocks:** Phase 12 (Pages Migration)
---
## 🎯 Goal
Migrate all search functionality from Supabase to Django's PostgreSQL full-text search with GIN indexes.
---
## 📋 Tasks
### Task 8.1: Search Service (3 hours)
**File:** `src/services/search/searchService.ts`
#### Create Core Service
```typescript
class SearchService extends BaseService {
// GET /search/?q={query}
async globalSearch(query: string, filters?: SearchFilters): Promise<SearchResults>
// GET /search/parks/?q={query}
async searchParks(query: string, filters?: ParkFilters): Promise<Park[]>
// GET /search/rides/?q={query}
async searchRides(query: string, filters?: RideFilters): Promise<Ride[]>
// GET /search/companies/?q={query}
async searchCompanies(query: string, filters?: CompanyFilters): Promise<Company[]>
// GET /search/ride-models/?q={query}
async searchRideModels(query: string, filters?: RideModelFilters): Promise<RideModel[]>
// GET /search/suggestions/?q={query}
async getSearchSuggestions(query: string): Promise<string[]>
}
```
#### Checklist
- [ ] Create `searchService.ts` with all methods
- [ ] Create types for search results
- [ ] Create mappers for search transformations
- [ ] Handle search ranking/relevance
- [ ] Implement debounced search
- [ ] Export from `src/services/search/index.ts`
---
### Task 8.2: Update Search Components (2 hours)
**File:** `src/components/search/SearchResults.tsx`
#### Replace Supabase Calls
```typescript
// OLD - Supabase
const { data } = await supabase
.from('parks')
.select('*')
.textSearch('name', query);
// NEW - Django Service
const parks = await searchService.searchParks(query, filters);
```
#### Checklist
- [ ] Replace `supabase.textSearch()` with `searchService.globalSearch()`
- [ ] Update search result display
- [ ] Update search highlighting
- [ ] Test search results rendering
- [ ] Verify pagination works
---
### Task 8.3: Update Filter Components (3 hours)
**Files to Update:**
- `src/components/parks/ParkFilters.tsx`
- `src/components/rides/RideFilters.tsx`
- `src/components/manufacturers/ManufacturerFilters.tsx`
- `src/components/operators/OperatorFilters.tsx`
- `src/components/designers/DesignerFilters.tsx`
#### Checklist
- [ ] Replace all Supabase filter queries with service calls
- [ ] Update park filters (type, location, status)
- [ ] Update ride filters (category, manufacturer, status)
- [ ] Update company filters (type, country)
- [ ] Test all filter combinations
- [ ] Verify filter state persistence
---
### Task 8.4: Search Autocomplete (2 hours)
**File:** `src/components/search/SearchAutocomplete.tsx`
#### Create/Update Autocomplete
- [ ] Implement search suggestions
- [ ] Debounce search input
- [ ] Display recent searches
- [ ] Handle keyboard navigation
- [ ] Test autocomplete behavior
---
## 🎯 Success Criteria
### Service Layer
- [ ] SearchService created and functional
- [ ] All search operations use service
- [ ] Search ranking works correctly
- [ ] Suggestions work
### Components
- [ ] Zero `supabase.textSearch()` calls
- [ ] Zero `supabase.from().ilike()` calls for search
- [ ] All filter components updated
- [ ] Search results display correctly
- [ ] Autocomplete works
### Testing
- [ ] Global search works
- [ ] Entity-specific search works
- [ ] Filters work correctly
- [ ] Search rankings are relevant
- [ ] Autocomplete suggests correctly
- [ ] Empty search handled gracefully
- [ ] No results handled gracefully
### Performance
- [ ] Search is fast (<500ms)
- [ ] Debouncing prevents excessive requests
- [ ] Results are properly cached
- [ ] Pagination works smoothly
---
## 📝 Implementation Notes
### Django Search Implementation
- Django uses PostgreSQL GIN indexes for full-text search
- Search vectors are automatically updated via signals
- Ranking is handled by PostgreSQL `ts_rank`
- Supports phrase search, AND/OR operators
- Accent-insensitive search
### Search Features
- **Global Search**: Searches across all entity types
- **Entity Search**: Searches within specific entity type
- **Filters**: Can be combined with search queries
- **Ranking**: Results sorted by relevance
- **Highlighting**: Search terms highlighted in results
### Debouncing
- Implement 300ms debounce on search input
- Cancel previous search requests
- Show loading state during search
- Cache recent search results
### Empty States
- Handle empty search query
- Handle no results found
- Suggest alternative spellings
- Show recent searches
---
## 🔗 Related Files
### Services
- `src/services/search/searchService.ts`
- `src/services/search/types.ts`
- `src/services/search/mappers.ts`
### Components
- `src/components/search/SearchResults.tsx`
- `src/components/search/SearchAutocomplete.tsx`
- `src/components/search/SearchFilters.tsx`
- All filter components in entity directories
### Hooks
- `src/hooks/useSearch.ts`
- `src/hooks/useSearchFilters.ts`
- `src/hooks/useSearchSuggestions.ts`
---
## ⏭️ Next Phase
**Phase 9:** Timeline & History - Migrate timeline events and entity history to Django.

View File

@@ -0,0 +1,81 @@
# PHASE 9: Timeline & History
**Status:** ⬜ Not Started
**Estimated Time:** 6-8 hours
**Priority:** MEDIUM
**Depends On:** Phase 1 (Foundation)
**Blocks:** Phase 12 (Pages Migration)
---
## 🎯 Goal
Migrate timeline events and entity history/versioning from Supabase to Django's pghistory system.
---
## 📋 Tasks
### Task 9.1: Timeline Service (2 hours)
```typescript
class TimelineService extends BaseService {
// GET /timeline/entity/{type}/{id}/
async getEntityTimeline(entityType: string, entityId: string): Promise<TimelineEvent[]>
// POST /timeline/entity/{type}/{id}/
async createTimelineEvent(entityType: string, entityId: string, data: TimelineEventData): Promise<TimelineEvent>
}
```
- [ ] Create `src/services/timeline/timelineService.ts`
- [ ] Update `src/components/timeline/EntityTimelineManager.tsx`
### Task 9.2: History Service (2 hours)
```typescript
class HistoryService extends BaseService {
// GET /history/{entity_type}/{entity_id}/
async getEntityHistory(entityType: string, entityId: string): Promise<HistoryEntry[]>
// GET /history/{entity_type}/{entity_id}/diff/?version1={v1}&version2={v2}
async getVersionDiff(entityType: string, entityId: string, v1: string, v2: string): Promise<VersionDiff>
// GET /history/recent/
async getRecentChanges(limit?: number): Promise<HistoryEntry[]>
}
```
- [ ] Create `src/services/history/historyService.ts`
- [ ] Replace `supabase.rpc('get_version_diff')`
- [ ] Replace `supabase.rpc('get_recent_changes')`
### Task 9.3: Update Components (2-3 hours)
- [ ] Update version comparison components
- [ ] Update history display components
- [ ] Update recent changes widget
- [ ] Remove all Supabase RPC calls
### Task 9.4: Homepage Integration (1 hour)
- [ ] Update `src/hooks/homepage/useHomepageRecentChanges.ts`
- [ ] Replace `supabase.rpc('get_recent_changes')`
- [ ] Test recent changes display
---
## 🎯 Success Criteria
- [ ] Timeline events display correctly
- [ ] Entity history works
- [ ] Version diffs work
- [ ] Recent changes widget works
- [ ] Zero `supabase.rpc()` calls for history
- [ ] Homepage shows recent changes
---
## ⏭️ Next Phase
**Phase 10:** Users & Profiles

View File

@@ -0,0 +1,110 @@
# PHASE 10: Users & Profiles
**Status:** ⬜ Not Started
**Estimated Time:** 8-10 hours
**Priority:** HIGH
**Depends On:** Phase 1 (Foundation), Phase 2 (Authentication)
**Blocks:** Phase 12 (Pages Migration)
---
## 🎯 Goal
Migrate all user profile and user management functionality from Supabase to Django.
---
## 📋 Tasks
### Task 10.1: Users Service (3 hours)
```typescript
class UsersService extends BaseService {
// GET /users/{id}/
async getUser(userId: string): Promise<User>
// GET /users/{id}/profile/
async getUserProfile(userId: string): Promise<UserProfile>
// PATCH /users/{id}/profile/
async updateUserProfile(userId: string, data: Partial<ProfileData>): Promise<UserProfile>
// GET /users/search/?q={query}
async searchUsers(query: string): Promise<User[]>
// POST /users/{id}/block/
async blockUser(userId: string): Promise<void>
// DELETE /users/{id}/block/
async unblockUser(userId: string): Promise<void>
// GET /users/{id}/blocked/
async getBlockedUsers(userId: string): Promise<User[]>
}
```
- [ ] Create `src/services/users/usersService.ts`
- [ ] Create types and mappers
- [ ] Handle profile updates
- [ ] Handle user blocking
### Task 10.2: Update Profile Pages (3 hours)
**Files:**
- `src/pages/Profile.tsx`
- `src/components/profile/UserReviewsList.tsx`
- `src/components/profile/RideCreditsManager.tsx`
- `src/components/profile/UserBlockButton.tsx`
- [ ] Replace `supabase.from('profiles')` with `usersService`
- [ ] Replace `supabase.rpc('get_filtered_profile')`
- [ ] Update profile editing
- [ ] Test profile display
### Task 10.3: Update Settings Pages (3 hours)
**Files:**
- `src/pages/Settings.tsx`
- `src/components/settings/AccountProfileTab.tsx`
- `src/components/settings/SecurityTab.tsx`
- `src/components/settings/PrivacyTab.tsx`
- `src/components/settings/LocationTab.tsx`
- `src/components/settings/DataExportTab.tsx`
- [ ] Replace all Supabase auth/profile calls
- [ ] Update email change flow
- [ ] Update password change flow
- [ ] Update account deletion flow
- [ ] Update data export
### Task 10.4: Update User Blocking (1-2 hours)
**Files:**
- `src/components/privacy/BlockedUsers.tsx`
- `src/components/profile/UserBlockButton.tsx`
- [ ] Replace block/unblock operations
- [ ] Update blocked users list
- [ ] Test blocking functionality
---
## 🎯 Success Criteria
- [ ] User profiles display correctly
- [ ] Profile editing works
- [ ] Settings pages work
- [ ] User blocking works
- [ ] Avatar uploads work
- [ ] Email change works
- [ ] Password change works
- [ ] Data export works
- [ ] Account deletion works
- [ ] Zero `supabase.from('profiles')` calls
- [ ] Zero `supabase.auth.updateUser()` calls (except in auth service)
---
## ⏭️ Next Phase
**Phase 11:** Contact & Reports

View File

@@ -0,0 +1,79 @@
# PHASE 11: Contact & Reports
**Status:** ⬜ Not Started
**Estimated Time:** 4-6 hours
**Priority:** MEDIUM
**Depends On:** Phase 1 (Foundation)
**Blocks:** Phase 12 (Pages Migration)
---
## 🎯 Goal
Complete migration of contact form and reports system to Django (reports already partially migrated).
---
## 📋 Tasks
### Task 11.1: Contact Service (2 hours)
```typescript
class ContactService extends BaseService {
// POST /contact/
async submitContact(data: ContactFormData): Promise<ContactSubmission>
// GET /contact/ (moderators only)
async getContactSubmissions(filters?: ContactFilters): Promise<ContactSubmission[]>
// PATCH /contact/{id}/status/ (moderators only)
async updateContactStatus(id: string, status: string, notes?: string): Promise<ContactSubmission>
}
```
- [ ] Create `src/services/contact/contactService.ts`
- [ ] Create types and mappers
### Task 11.2: Update Contact Form (1 hour)
**File:** `src/components/contact/ContactForm.tsx`
- [ ] Replace `supabase.from('contact_submissions')` with `contactService`
- [ ] Update form submission
- [ ] Test contact form
- [ ] Verify confirmation emails
### Task 11.3: Verify Reports Service (1 hour)
**Note:** Reports service was already created in earlier work, but verify it's complete.
- [ ] Verify `src/services/reports/reportsService.ts` is complete
- [ ] Verify `src/components/moderation/ReportButton.tsx` uses service
- [ ] Verify `src/components/moderation/ReportsQueue.tsx` uses service
- [ ] Test report submission
- [ ] Test report management
### Task 11.4: Final Contact/Reports Testing (1-2 hours)
- [ ] Test contact form submission
- [ ] Test contact admin management
- [ ] Test report submission
- [ ] Test report moderation
- [ ] Verify emails are sent
---
## 🎯 Success Criteria
- [ ] Contact form works
- [ ] Contact emails sent
- [ ] Reports fully functional
- [ ] Admin can manage contacts/reports
- [ ] Zero `supabase.from('contact_submissions')` calls
- [ ] Zero `supabase.from('reports')` calls
---
## ⏭️ Next Phase
**Phase 12:** Pages Migration - THE BIG ONE

View File

@@ -0,0 +1,724 @@
# PHASE 12: Next.js 15 App Router Pages Migration
**Status:** ⬜ Not Started
**Estimated Time:** 45-55 hours
**Priority:** CRITICAL
**Depends On:** All previous phases (1-11)
**Blocks:** Phase 13 (Next.js Optimization)
---
## 🎯 Goal
Convert ALL React SPA pages to Next.js 15 App Router pages while replacing Supabase calls with Django services. This is a DUAL migration:
1. **React → Next.js App Router**: Convert pages and routing
2. **Supabase → Django**: Replace all data fetching
**Critical Requirements:**
- Preserve ALL existing URLs
- Maintain exact same UI/UX
- Use Server Components by default
- Mark Client Components with 'use client'
- Sacred Pipeline remains intact
---
## 📋 Next.js App Router Structure
### New Directory Structure
```
app/
├── layout.tsx # Root layout
├── page.tsx # Homepage
├── loading.tsx # Global loading
├── error.tsx # Global error
├── not-found.tsx # 404 page
├── parks/
│ ├── page.tsx # /parks (listing)
│ ├── loading.tsx # Loading state
│ ├── [parkSlug]/
│ │ ├── page.tsx # /parks/[parkSlug]
│ │ ├── loading.tsx
│ │ └── rides/
│ │ └── page.tsx # /parks/[parkSlug]/rides
│ └── owners/
│ └── [ownerSlug]/
│ └── page.tsx # /owners/[ownerSlug]/parks
├── rides/
│ ├── page.tsx # /rides (listing)
│ ├── [rideSlug]/
│ │ ├── page.tsx # /rides/[rideSlug]
│ │ └── reviews/
│ │ └── page.tsx # /rides/[rideSlug]/reviews
│ └── models/
│ └── [modelSlug]/
│ ├── page.tsx # /ride-models/[modelSlug]
│ └── rides/
│ └── page.tsx # /ride-models/[modelSlug]/rides
├── manufacturers/
│ ├── page.tsx # /manufacturers (listing)
│ └── [manufacturerSlug]/
│ ├── page.tsx # /manufacturers/[manufacturerSlug]
│ └── rides/
│ └── page.tsx # /manufacturers/[manufacturerSlug]/rides
├── owners/
│ ├── page.tsx # /owners (listing)
│ └── [ownerSlug]/
│ └── page.tsx # /owners/[ownerSlug]
├── designers/
│ ├── page.tsx # /designers (listing)
│ └── [designerSlug]/
│ └── page.tsx # /designers/[designerSlug]
├── auth/
│ ├── login/
│ │ └── page.tsx
│ ├── register/
│ │ └── page.tsx
│ └── callback/
│ └── page.tsx
├── profile/
│ ├── page.tsx # /profile
│ ├── settings/
│ │ └── page.tsx
│ └── lists/
│ └── page.tsx
├── admin/
│ ├── page.tsx # /admin
│ └── moderation/
│ └── page.tsx # /admin/moderation
├── search/
│ └── page.tsx # /search
└── contact/
└── page.tsx # /contact
```
---
## 📋 Tasks
### Task 12.1: Root Layout & Homepage (6 hours)
#### Create Root Layout
`app/layout.tsx` (Server Component):
```typescript
import { Inter } from 'next/font/google';
import './globals.css';
import { AuthProvider } from '@/components/providers/AuthProvider';
import { Navigation } from '@/components/layout/Navigation';
import { Footer } from '@/components/layout/Footer';
const inter = Inter({ subsets: ['latin'] });
export const metadata = {
title: 'ThrillWiki - Theme Park & Ride Database',
description: 'Comprehensive database of theme parks and rides',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<AuthProvider>
<Navigation />
<main className="min-h-screen">{children}</main>
<Footer />
</AuthProvider>
</body>
</html>
);
}
```
#### Convert Homepage
`app/page.tsx` (Server Component):
```typescript
import { env } from '@/lib/env';
import { FeaturedParks } from '@/components/home/FeaturedParks';
import { RecentReviews } from '@/components/home/RecentReviews';
import { Stats } from '@/components/home/Stats';
export default async function HomePage() {
// Fetch data server-side
const [parks, stats] = await Promise.all([
fetch(`${env.NEXT_PUBLIC_DJANGO_API_URL}/parks/?featured=true`).then(r => r.json()),
fetch(`${env.NEXT_PUBLIC_DJANGO_API_URL}/stats/`).then(r => r.json()),
]);
return (
<div>
<Hero />
<Stats data={stats} />
<FeaturedParks parks={parks} />
<RecentReviews />
</div>
);
}
```
#### Checklist
- [ ] Create `app/layout.tsx` with root layout
- [ ] Create `app/page.tsx` for homepage
- [ ] Create `app/loading.tsx` for loading state
- [ ] Create `app/error.tsx` for error boundary
- [ ] Create `app/not-found.tsx` for 404
- [ ] Move CSS to `app/globals.css`
- [ ] Test homepage loads
- [ ] Verify navigation works
- [ ] Check loading states
- [ ] Test error boundaries
---
### Task 12.2: Park Pages (8 hours)
#### Park Listing Page
`app/parks/page.tsx` (Server Component):
```typescript
import { env } from '@/lib/env';
import { ParksList } from '@/components/parks/ParksList';
import { ParkFilters } from '@/components/parks/ParkFilters';
export const metadata = {
title: 'Theme Parks - ThrillWiki',
description: 'Browse theme parks from around the world',
};
export default async function ParksPage({
searchParams,
}: {
searchParams: { [key: string]: string | undefined };
}) {
const params = new URLSearchParams(searchParams as any);
const parks = await fetch(
`${env.NEXT_PUBLIC_DJANGO_API_URL}/parks/?${params}`,
{ next: { revalidate: 300 } } // Cache for 5 minutes
).then(r => r.json());
return (
<div>
<h1>Theme Parks</h1>
<ParkFilters /> {/* Client Component */}
<ParksList parks={parks} /> {/* Can be Server Component */}
</div>
);
}
```
#### Park Detail Page
`app/parks/[parkSlug]/page.tsx` (Server Component):
```typescript
import { env } from '@/lib/env';
import { ParkDetail } from '@/components/parks/ParkDetail';
import { notFound } from 'next/navigation';
export async function generateStaticParams() {
// Pre-render top 100 parks
const parks = await fetch(`${env.NEXT_PUBLIC_DJANGO_API_URL}/parks/?page_size=100`)
.then(r => r.json());
return parks.results.map((park: any) => ({
parkSlug: park.slug,
}));
}
export async function generateMetadata({ params }: { params: { parkSlug: string } }) {
const park = await fetch(`${env.NEXT_PUBLIC_DJANGO_API_URL}/parks/${params.parkSlug}/`)
.then(r => r.json());
return {
title: `${park.name} - ThrillWiki`,
description: park.description,
};
}
export default async function ParkDetailPage({
params
}: {
params: { parkSlug: string }
}) {
const park = await fetch(
`${env.NEXT_PUBLIC_DJANGO_API_URL}/parks/${params.parkSlug}/`,
{ next: { revalidate: 3600 } } // Cache for 1 hour
).then(r => r.json())
.catch(() => notFound());
return <ParkDetail park={park} />;
}
```
#### Checklist
- [ ] Create `app/parks/page.tsx`
- [ ] Create `app/parks/[parkSlug]/page.tsx`
- [ ] Create `app/parks/[parkSlug]/rides/page.tsx`
- [ ] Create `app/owners/[ownerSlug]/parks/page.tsx`
- [ ] Create loading.tsx for each route
- [ ] Implement generateStaticParams for ISR
- [ ] Implement generateMetadata for SEO
- [ ] Test all park URLs work
- [ ] Verify filtering works
- [ ] Test pagination
---
### Task 12.3: Ride Pages (8 hours)
#### Ride Listing Page
`app/rides/page.tsx` (Server Component with caching)
#### Ride Detail Page
`app/rides/[rideSlug]/page.tsx` (Server Component)
#### Ride Model Pages
`app/ride-models/[modelSlug]/page.tsx` (Server Component)
#### Checklist
- [ ] Create `app/rides/page.tsx`
- [ ] Create `app/rides/[rideSlug]/page.tsx`
- [ ] Create `app/rides/[rideSlug]/reviews/page.tsx`
- [ ] Create `app/ride-models/[modelSlug]/page.tsx`
- [ ] Create `app/ride-models/[modelSlug]/rides/page.tsx`
- [ ] Create loading states
- [ ] Implement metadata
- [ ] Test all ride URLs
- [ ] Verify reviews work
---
### Task 12.4: Company Pages (8 hours)
Convert manufacturer, owner, and designer pages to App Router.
#### Checklist
- [ ] Create `app/manufacturers/page.tsx`
- [ ] Create `app/manufacturers/[manufacturerSlug]/page.tsx`
- [ ] Create `app/manufacturers/[manufacturerSlug]/rides/page.tsx`
- [ ] Create `app/owners/page.tsx`
- [ ] Create `app/owners/[ownerSlug]/page.tsx`
- [ ] Create `app/designers/page.tsx`
- [ ] Create `app/designers/[designerSlug]/page.tsx`
- [ ] Test all company URLs
- [ ] Verify filtering works
---
### Task 12.5: User Pages (6 hours)
These pages need authentication and user-specific data.
#### Profile Page
`app/profile/page.tsx` (Server Component with auth check):
```typescript
import { redirect } from 'next/navigation';
import { getServerSession } from 'next-auth';
import { ProfileView } from '@/components/profile/ProfileView';
export default async function ProfilePage() {
const session = await getServerSession();
if (!session) {
redirect('/auth/login');
}
const user = await fetch(
`${env.NEXT_PUBLIC_DJANGO_API_URL}/users/me/`,
{
headers: { Authorization: `Bearer ${session.accessToken}` },
cache: 'no-store' // Don't cache user-specific data
}
).then(r => r.json());
return <ProfileView user={user} />;
}
```
#### Checklist
- [ ] Create `app/profile/page.tsx`
- [ ] Create `app/profile/settings/page.tsx`
- [ ] Create `app/profile/lists/page.tsx`
- [ ] Implement authentication checks
- [ ] Disable caching for user data
- [ ] Test profile loads
- [ ] Test settings work
- [ ] Test list management
---
### Task 12.6: Admin & Moderation Pages (6 hours)
Admin pages require role checks and real-time updates.
#### Moderation Queue
`app/admin/moderation/page.tsx` (Server Component):
```typescript
import { redirect } from 'next/navigation';
import { getServerSession } from 'next-auth';
import { ModerationQueue } from '@/components/moderation/ModerationQueue';
export default async function ModerationPage() {
const session = await getServerSession();
if (!session || session.user.role !== 'moderator') {
redirect('/');
}
// Fetch submissions server-side
const submissions = await fetch(
`${env.NEXT_PUBLIC_DJANGO_API_URL}/moderation/queue/`,
{
headers: { Authorization: `Bearer ${session.accessToken}` },
cache: 'no-store'
}
).then(r => r.json());
return <ModerationQueue initialSubmissions={submissions} />;
}
```
#### Checklist
- [ ] Create `app/admin/page.tsx`
- [ ] Create `app/admin/moderation/page.tsx`
- [ ] Implement role-based access
- [ ] Add server-side permission checks
- [ ] Test moderator access
- [ ] Test admin dashboard
- [ ] Verify Sacred Pipeline
---
### Task 12.7: Authentication Pages (4 hours)
#### Login Page
`app/auth/login/page.tsx` (Client Component - needs form state):
```typescript
'use client';
import { LoginForm } from '@/components/auth/LoginForm';
import { redirect } from 'next/navigation';
import { useSession } from 'next-auth/react';
export default function LoginPage() {
const { data: session } = useSession();
if (session) {
redirect('/profile');
}
return (
<div>
<h1>Login</h1>
<LoginForm />
</div>
);
}
```
#### Checklist
- [ ] Create `app/auth/login/page.tsx`
- [ ] Create `app/auth/register/page.tsx`
- [ ] Create `app/auth/callback/page.tsx`
- [ ] Create `app/auth/reset-password/page.tsx`
- [ ] Test email/password login
- [ ] Test OAuth login (Google, GitHub)
- [ ] Test registration
- [ ] Test password reset
---
### Task 12.8: Search & Contact Pages (3 hours)
#### Search Page
`app/search/page.tsx` (Client Component - needs interactive search):
```typescript
'use client';
import { useSearchParams } from 'next/navigation';
import { SearchResults } from '@/components/search/SearchResults';
import { SearchFilters } from '@/components/search/SearchFilters';
export default function SearchPage() {
const searchParams = useSearchParams();
const query = searchParams.get('q');
return (
<div>
<h1>Search Results</h1>
<SearchFilters />
<SearchResults query={query} />
</div>
);
}
```
#### Contact Page
`app/contact/page.tsx` (Server Component):
```typescript
import { ContactForm } from '@/components/contact/ContactForm';
export const metadata = {
title: 'Contact Us - ThrillWiki',
};
export default function ContactPage() {
return (
<div>
<h1>Contact Us</h1>
<ContactForm />
</div>
);
}
```
#### Checklist
- [ ] Create `app/search/page.tsx`
- [ ] Create `app/contact/page.tsx`
- [ ] Test search functionality
- [ ] Test contact form submission
- [ ] Verify form validation
---
### Task 12.9: Component Migration (6 hours)
Convert React components to work with Next.js.
#### Server Components (Default)
Components that only display data and don't need interactivity:
- Lists (ParksL, RidesList)
- Detail views (ParkDetail, RideDetail)
- Static content
#### Client Components ('use client')
Components that need interactivity:
- Forms (LoginForm, SubmissionForm)
- Interactive filters
- Modals and dialogs
- Components using useState, useEffect, etc.
#### Checklist
- [ ] Identify all components using Supabase
- [ ] Determine Server vs Client Component
- [ ] Add 'use client' where needed
- [ ] Replace Supabase with service calls
- [ ] Test all components render
- [ ] Verify interactions work
- [ ] Check no hydration errors
---
### Task 12.10: Routing & Navigation (4 hours)
Update all navigation to use Next.js routing.
#### Replace React Router
```typescript
// OLD (React Router)
import { Link, useNavigate } from 'react-router-dom';
// NEW (Next.js)
import Link from 'next/link';
import { useRouter } from 'next/navigation';
```
#### Update All Links
```typescript
// OLD
<Link to="/parks">Parks</Link>
// NEW
<Link href="/parks">Parks</Link>
```
#### Checklist
- [ ] Replace all React Router imports
- [ ] Update all <Link> components
- [ ] Update all navigate() calls to router.push()
- [ ] Update all useParams to use params prop
- [ ] Update all useSearchParams
- [ ] Test navigation works
- [ ] Test browser back button
- [ ] Test deep linking
---
## 🎯 Success Criteria
### Zero React Router Usage
- [ ] No `react-router-dom` imports remain
- [ ] All navigation uses Next.js Link/router
- [ ] No old React pages in `src/pages/` (move to `app/`)
### Zero Supabase Usage
- [ ] `grep -r "supabase\." app/ components/ lib/ --include="*.ts" --include="*.tsx"` returns 0
- [ ] `grep -r "from '@/lib/supabaseClient'"` returns 0
- [ ] `grep -r "from '@supabase/supabase-js'"` returns 0
### All URLs Preserved
- [ ] `/parks` works
- [ ] `/parks/[parkSlug]` works
- [ ] `/parks/[parkSlug]/rides` works
- [ ] `/owners/[ownerSlug]/parks` works
- [ ] `/rides` works
- [ ] `/rides/[rideSlug]` works
- [ ] `/ride-models/[modelSlug]` works
- [ ] `/manufacturers/[manufacturerSlug]` works
- [ ] `/manufacturers/[manufacturerSlug]/rides` works
- [ ] All other URLs work
### All Pages Load
- [ ] Homepage loads (SSR)
- [ ] All park pages load
- [ ] All ride pages load
- [ ] All company pages load
- [ ] Profile page loads (auth check)
- [ ] Settings page loads (auth check)
- [ ] Admin dashboard loads (role check)
- [ ] Moderation queue loads (role check)
- [ ] Search page loads
- [ ] Contact page loads
- [ ] Auth pages load
### All Features Work
- [ ] Can browse entities
- [ ] Can view entity details
- [ ] Can filter/sort
- [ ] Can submit content (creates submission)
- [ ] Can moderate content
- [ ] Can write reviews
- [ ] Can add ride credits
- [ ] Can manage top lists
- [ ] Can upload photos
- [ ] Can search
- [ ] Authentication works
- [ ] Server-side rendering works
- [ ] Client-side navigation works
### Next.js Specific
- [ ] Server Components render on server
- [ ] Client Components work in browser
- [ ] No hydration errors
- [ ] Loading states display
- [ ] Error boundaries catch errors
- [ ] Metadata API generates correct tags
- [ ] ISR/SSR configured correctly
- [ ] Build succeeds without errors
### Sacred Pipeline Intact
- [ ] All entity changes go through submissions
- [ ] Moderation queue receives submissions
- [ ] Approval creates entities/updates
- [ ] Rejection saves reason
- [ ] No pipeline bypasses
---
## 📝 Implementation Strategy
### 1. Start with Entity Pages (Most Critical)
Focus on parks, rides, companies first since they're core functionality.
### 2. Then User Pages
Profile and settings are user-facing and important.
### 3. Then Admin Pages
Moderation queue and admin dashboard.
### 4. Then Misc Pages
Homepage, search, contact, etc.
### 5. Component Sweep
Find any remaining components with Supabase calls.
### 6. Final Search
Run grep commands to find ANY remaining Supabase usage.
---
## 🚨 Critical Rules
### DO NOT
- ❌ Skip any page
- ❌ Leave any `supabase.` calls
- ❌ Assume a page works without testing it
- ❌ Move to Phase 13 until grep returns 0 results
### MUST DO
- ✅ Test EVERY page you touch
- ✅ Verify EVERY interaction works
- ✅ Check console for errors
- ✅ Verify Sacred Pipeline intact
- ✅ Document any issues found
---
## 🔗 Related Documentation
Refer back to previous phases for service usage examples:
- Phase 1: Foundation (BaseService pattern)
- Phase 4: Entity Services (parks, rides, companies)
- Phase 5: Reviews & Social
- Phase 6: Moderation & Admin
- Phase 7: Media & Photos
- Phase 8: Search
- Phase 9: Timeline & History
- Phase 10: Users & Profiles
- Phase 11: Contact & Reports
---
## ⏭️ Next Phase
Once this phase is complete, proceed to [Phase 13: Next.js Optimization](./PHASE_13_NEXTJS_OPTIMIZATION.md)
---
## 🔗 Related Documentation
- [Next.js 15 Migration Guide](./NEXTJS_15_MIGRATION_GUIDE.md)
- [Environment Variables Guide](./ENVIRONMENT_VARIABLES.md)
- [Phase 1: Foundation](./PHASE_01_FOUNDATION.md)
- [Phase 13: Next.js Optimization](./PHASE_13_NEXTJS_OPTIMIZATION.md)
- Next.js App Router: https://nextjs.org/docs/app
- Server Components: https://nextjs.org/docs/app/building-your-application/rendering/server-components
---
**Document Version:** 2.0
**Last Updated:** November 9, 2025
**Changes:** Converted to Next.js 15 App Router migration

View File

@@ -0,0 +1,547 @@
# PHASE 13: Next.js 15 Optimization
**Status:** ⬜ Not Started
**Estimated Time:** 15-20 hours
**Priority:** HIGH
**Depends On:** Phase 12 (Next.js Pages Migration)
**Blocks:** Phase 14 (Cleanup & Testing)
---
## 🎯 Phase Goal
Optimize the Next.js 15 application for production with Turbopack, implement advanced features, and ensure peak performance.
---
## 📋 Prerequisites
- [ ] Phase 12 complete (all pages migrated to Next.js App Router)
- [ ] All services converted from Supabase to Django API
- [ ] Sacred Pipeline working in Next.js
- [ ] Bun is configured as package manager
- [ ] Environment variables properly configured
---
## ✅ Task 13.1: Turbopack Configuration (3 hours)
### Configure next.config.js for Turbopack
Create/update `next.config.js`:
```javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
// Enable Turbopack for development
experimental: {
turbo: {
rules: {
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
resolveAlias: {
'@': './src',
},
},
},
// Image optimization
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'imagedelivery.net', // CloudFlare Images
},
{
protocol: 'https',
hostname: process.env.NEXT_PUBLIC_DJANGO_API_URL?.replace('https://', '') || 'api.thrillwiki.com',
},
],
formats: ['image/avif', 'image/webp'],
},
// Environment variables
env: {
NEXT_PUBLIC_DJANGO_API_URL: process.env.NEXT_PUBLIC_DJANGO_API_URL,
},
// Production optimizations
compress: true,
poweredByHeader: false,
reactStrictMode: true,
// Output configuration
output: 'standalone', // For Docker deployment
};
module.exports = nextConfig;
```
### Checklist
- [ ] `next.config.js` created/updated
- [ ] Turbopack rules configured
- [ ] Image optimization configured for CloudFlare
- [ ] Environment variables properly referenced
- [ ] Production flags enabled
- [ ] Development server uses Turbopack
- [ ] Hot reload works correctly
---
## ✅ Task 13.2: Server vs Client Component Optimization (4 hours)
### Identify Component Types
**Server Components (Default):**
- Data fetching pages
- Static content
- SEO-critical pages
- No user interaction
**Client Components (Need 'use client'):**
- Forms with state
- Interactive UI elements
- Hooks (useState, useEffect, etc.)
- Event handlers
- Context providers
### Optimization Checklist
#### Server Components
- [ ] All data-fetching pages are Server Components
- [ ] Park listing page is Server Component
- [ ] Ride listing page is Server Component
- [ ] Detail pages use Server Components for data
- [ ] SEO metadata generated server-side
#### Client Components
- [ ] Form components marked with 'use client'
- [ ] Interactive UI (modals, dropdowns) marked
- [ ] Authentication components marked
- [ ] Submission forms marked
- [ ] No unnecessary 'use client' directives
#### Component Structure
```typescript
// Good: Server Component with Client Components inside
// app/parks/page.tsx
export default async function ParksPage() {
const parks = await fetch(API_URL).then(r => r.json());
return (
<div>
<h1>Parks</h1>
<ParkFilters /> {/* Client Component */}
<ParksList parks={parks} /> {/* Can be Server Component */}
</div>
);
}
// Client Component for interactivity
// components/ParkFilters.tsx
'use client';
export function ParkFilters() {
const [filters, setFilters] = useState({});
// ...
}
```
---
## ✅ Task 13.3: Data Fetching Optimization (3 hours)
### Implement Caching Strategies
```typescript
// app/parks/[parkSlug]/page.tsx
export async function generateStaticParams() {
// Pre-render top 100 parks at build time
const parks = await fetch(`${API_URL}/parks/?page_size=100`);
return parks.results.map((park) => ({
parkSlug: park.slug,
}));
}
export default async function ParkDetailPage({
params
}: {
params: { parkSlug: string }
}) {
// Cache for 1 hour, revalidate in background
const park = await fetch(`${API_URL}/parks/${params.parkSlug}/`, {
next: { revalidate: 3600 }
}).then(r => r.json());
return <ParkDetail park={park} />;
}
```
### Caching Strategy Checklist
- [ ] Static pages use ISR (Incremental Static Regeneration)
- [ ] Dynamic pages have appropriate revalidation times
- [ ] Listing pages cache for 5-10 minutes
- [ ] Detail pages cache for 1 hour
- [ ] User-specific pages are not cached
- [ ] Search results are not cached
### Parallel Data Fetching
```typescript
// Fetch multiple resources in parallel
export default async function RideDetailPage({ params }) {
const [ride, reviews, photos] = await Promise.all([
fetch(`${API_URL}/rides/${params.rideSlug}/`),
fetch(`${API_URL}/rides/${params.rideSlug}/reviews/`),
fetch(`${API_URL}/rides/${params.rideSlug}/photos/`),
]);
return <RideDetail ride={ride} reviews={reviews} photos={photos} />;
}
```
- [ ] Parallel fetching implemented where possible
- [ ] No waterfall requests
- [ ] Loading states for slow requests
---
## ✅ Task 13.4: Loading States & Suspense (2 hours)
### Create Loading Components
```typescript
// app/parks/loading.tsx
export default function Loading() {
return (
<div className="space-y-4">
<div className="h-8 bg-gray-200 rounded animate-pulse" />
<div className="grid grid-cols-3 gap-4">
{[...Array(6)].map((_, i) => (
<div key={i} className="h-48 bg-gray-200 rounded animate-pulse" />
))}
</div>
</div>
);
}
```
### Implement Streaming
```typescript
// app/parks/[parkSlug]/page.tsx
import { Suspense } from 'react';
export default async function ParkPage({ params }) {
return (
<div>
<Suspense fallback={<ParkHeaderSkeleton />}>
<ParkHeader slug={params.parkSlug} />
</Suspense>
<Suspense fallback={<RidesListSkeleton />}>
<RidesList parkSlug={params.parkSlug} />
</Suspense>
</div>
);
}
```
### Checklist
- [ ] Loading.tsx for each major route
- [ ] Suspense boundaries for slow components
- [ ] Skeleton screens match final UI
- [ ] Streaming SSR enabled
- [ ] No flash of loading state
---
## ✅ Task 13.5: Metadata API for SEO (2 hours)
### Implement Dynamic Metadata
```typescript
// app/parks/[parkSlug]/page.tsx
import { Metadata } from 'next';
export async function generateMetadata({
params
}: {
params: { parkSlug: string }
}): Promise<Metadata> {
const park = await fetch(`${API_URL}/parks/${params.parkSlug}/`)
.then(r => r.json());
return {
title: `${park.name} - ThrillWiki`,
description: park.description || `Discover ${park.name}, a ${park.park_type} featuring exciting rides and attractions.`,
openGraph: {
title: park.name,
description: park.description,
images: [
{
url: park.banner_image_url || '/og-image.png',
width: 1200,
height: 630,
alt: park.name,
},
],
type: 'website',
siteName: 'ThrillWiki',
},
twitter: {
card: 'summary_large_image',
title: park.name,
description: park.description,
images: [park.banner_image_url || '/og-image.png'],
},
alternates: {
canonical: `https://thrillwiki.com/parks/${park.slug}`,
},
};
}
```
### SEO Checklist
- [ ] All pages have unique titles
- [ ] All pages have meta descriptions
- [ ] OpenGraph tags for social sharing
- [ ] Twitter Card tags
- [ ] Canonical URLs
- [ ] Structured data (JSON-LD)
- [ ] Robots.txt configured
- [ ] Sitemap.xml generated
---
## ✅ Task 13.6: Performance Optimization (3 hours)
### Bundle Analysis
```bash
# Install bundle analyzer
bun add @next/bundle-analyzer
# Update next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer(nextConfig);
# Run analysis
ANALYZE=true bun run build
```
### Code Splitting
```typescript
// Use dynamic imports for heavy components
import dynamic from 'next/dynamic';
const HeavyChart = dynamic(() => import('./HeavyChart'), {
loading: () => <p>Loading chart...</p>,
ssr: false, // Don't render on server
});
```
### Performance Checklist
- [ ] Bundle size < 500KB initial load
- [ ] Code splitting for large dependencies
- [ ] Images use next/image
- [ ] Fonts optimized with next/font
- [ ] CSS modules for component styles
- [ ] No unused dependencies
- [ ] Tree shaking working
### Core Web Vitals Targets
- [ ] LCP (Largest Contentful Paint) < 2.5s
- [ ] FID (First Input Delay) < 100ms
- [ ] CLS (Cumulative Layout Shift) < 0.1
- [ ] TTFB (Time to First Byte) < 600ms
---
## ✅ Task 13.7: Environment Variables & Security (1 hour)
### Environment Configuration
Create `.env.local`:
```bash
# Django API (required for build)
NEXT_PUBLIC_DJANGO_API_URL=https://api.thrillwiki.com
# CloudFlare (public)
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID=xxx
# Server-only secrets (no NEXT_PUBLIC_ prefix)
CLOUDFLARE_API_TOKEN=xxx
DJANGO_SECRET_KEY=xxx
```
### Security Checklist
- [ ] No secrets in client-side code
- [ ] Server-only vars don't have NEXT_PUBLIC_ prefix
- [ ] .env.local in .gitignore
- [ ] .env.example documented
- [ ] Environment validation on startup
- [ ] Sensitive data not logged
### Runtime Validation
```typescript
// lib/env.ts
import { z } from 'zod';
const envSchema = z.object({
NEXT_PUBLIC_DJANGO_API_URL: z.string().url(),
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID: z.string().min(1),
});
export const env = envSchema.parse({
NEXT_PUBLIC_DJANGO_API_URL: process.env.NEXT_PUBLIC_DJANGO_API_URL,
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID: process.env.NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID,
});
```
---
## ✅ Task 13.8: Error Boundaries & Monitoring (2 hours)
### Global Error Handling
```typescript
// app/error.tsx
'use client';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={reset}>Try again</button>
</div>
);
}
// app/global-error.tsx (catches errors in root layout)
'use client';
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<html>
<body>
<h2>Something went wrong!</h2>
<button onClick={reset}>Try again</button>
</body>
</html>
);
}
```
### Not Found Pages
```typescript
// app/not-found.tsx
export default function NotFound() {
return (
<div>
<h2>404 - Page Not Found</h2>
<p>The page you're looking for doesn't exist.</p>
</div>
);
}
```
### Checklist
- [ ] Error boundaries for each route segment
- [ ] Global error handler
- [ ] 404 page styled
- [ ] Error logging implemented
- [ ] User-friendly error messages
- [ ] Errors don't expose sensitive data
---
## 🎯 Phase Completion Criteria
### Configuration
- [ ] Turbopack fully configured
- [ ] Image optimization working
- [ ] Environment variables validated
- [ ] next.config.js optimized
### Performance
- [ ] Bundle size optimized
- [ ] Code splitting implemented
- [ ] Images optimized
- [ ] Fonts optimized
- [ ] Core Web Vitals meet targets
### SEO
- [ ] All pages have metadata
- [ ] OpenGraph tags present
- [ ] Sitemap generated
- [ ] Robots.txt configured
### User Experience
- [ ] Loading states implemented
- [ ] Error boundaries working
- [ ] 404 pages styled
- [ ] No jarring layout shifts
### Code Quality
- [ ] Server/Client components optimized
- [ ] No unnecessary 'use client' directives
- [ ] Parallel data fetching where possible
- [ ] Proper caching strategies
---
## 📊 Progress Tracking
**Started:** [Date]
**Completed:** [Date]
**Time Spent:** [Hours]
### Tasks Completed
- [ ] Task 13.1: Turbopack Configuration
- [ ] Task 13.2: Server vs Client Components
- [ ] Task 13.3: Data Fetching Optimization
- [ ] Task 13.4: Loading States & Suspense
- [ ] Task 13.5: Metadata API for SEO
- [ ] Task 13.6: Performance Optimization
- [ ] Task 13.7: Environment Variables
- [ ] Task 13.8: Error Boundaries
---
## ⏭️ Next Phase
Once this phase is complete, proceed to [Phase 14: Cleanup & Testing](./PHASE_14_CLEANUP_TESTING.md)
---
## 🔗 Related Documentation
- [Next.js 15 Migration Guide](./NEXTJS_15_MIGRATION_GUIDE.md)
- [Environment Variables Guide](./ENVIRONMENT_VARIABLES.md)
- [Bun Setup Guide](./BUN_GUIDE.md)
- Next.js Documentation: https://nextjs.org/docs
---
**Document Version:** 1.0
**Last Updated:** November 9, 2025

View File

@@ -0,0 +1,409 @@
# PHASE 14: Cleanup & Testing
**Status:** ⬜ Not Started
**Estimated Time:** 15-18 hours
**Priority:** CRITICAL
**Depends On:** Phase 13 (Next.js Optimization)
**Blocks:** Production Deployment
---
## 🎯 Goal
Complete removal of all Supabase dependencies, verify 100% functionality in Next.js 15, and ensure production readiness. This is the FINAL phase.
---
## 📋 Tasks
### Task 14.1: Remove Supabase Files & Dependencies (3 hours)
#### Step 1: Delete Supabase Integration Directory
```bash
rm -rf src/integrations/supabase/
```
#### Step 2: Delete Supabase Client Files
```bash
rm src/lib/supabaseClient.ts
rm src/lib/supabaseHelpers.ts
```
#### Step 3: Remove Package Dependency
```bash
bun remove @supabase/supabase-js
bun install # Update lock file
```
#### Step 4: Clean Environment Variables
Edit `.env.local` and `.env.example`:
- [ ] Remove `NEXT_PUBLIC_SUPABASE_URL`
- [ ] Remove `NEXT_PUBLIC_SUPABASE_ANON_KEY`
- [ ] Verify `NEXT_PUBLIC_DJANGO_API_URL` is set correctly
- [ ] Verify all required Next.js env vars present
#### Checklist
- [ ] `src/integrations/supabase/` deleted
- [ ] `src/lib/supabaseClient.ts` deleted
- [ ] `src/lib/supabaseHelpers.ts` deleted
- [ ] `@supabase/supabase-js` removed from `package.json`
- [ ] `package-lock.json` updated
- [ ] Supabase env vars removed
- [ ] Django API URL configured
---
### Task 14.2: Final Verification (2 hours)
Run these commands - ALL MUST RETURN ZERO RESULTS:
```bash
# 1. Check for Supabase imports
grep -r "from '@supabase/supabase-js'" src/ --include="*.ts" --include="*.tsx"
# Expected: No results
# 2. Check for Supabase client imports
grep -r "from '@/lib/supabaseClient'" src/ --include="*.ts" --include="*.tsx"
# Expected: No results
# 3. Check for any supabase usage (excluding comments)
grep -r "supabase\." src/ --include="*.ts" --include="*.tsx" | grep -v "//"
# Expected: No results (or only in comments)
# 4. Check package.json
cat package.json | grep supabase
# Expected: No results
# 5. Check for Supabase types
grep -r "import.*Database.*from.*supabase" src/
# Expected: No results
```
#### Checklist
- [ ] Zero Supabase imports found
- [ ] Zero Supabase usage found
- [ ] Zero Supabase in package.json
- [ ] Zero Supabase types imported
---
### Task 14.3: TypeScript Compilation (1 hour)
```bash
# Run TypeScript compiler with Next.js
bun run type-check
# Or using Next.js lint
bun run lint
```
#### Checklist
- [ ] TypeScript compiles without errors
- [ ] All imports resolve correctly
- [ ] All types are defined
- [ ] No `any` types added (follow TYPESCRIPT_ANY_POLICY.md)
---
### Task 14.4: Next.js Build Test (2 hours)
```bash
# Production build with Turbopack
bun run build
# Verify build output
ls -la .next/
```
#### Checklist
- [ ] Build completes successfully
- [ ] No build errors
- [ ] No build warnings (or acceptable warnings only)
- [ ] `.next/` directory generated correctly
- [ ] Turbopack optimization applied
- [ ] Server components compiled
- [ ] Client components bundled separately
- [ ] Static pages generated (if using ISR/SSG)
- [ ] Build size is reasonable
#### Next.js Specific Checks
- [ ] Server components work in build
- [ ] Client components work in build
- [ ] API routes function
- [ ] Metadata API generates correct tags
- [ ] Image optimization working
- [ ] Font optimization working
---
### Task 14.5: Integration Testing (5 hours)
**Run tests in both development and production modes:**
```bash
# Development mode
bun run dev
# Production mode
bun run build && bun run start
```
Test EVERY major flow:
#### Authentication (30 min)
- [ ] Register new user
- [ ] Login with email/password
- [ ] Login with Google OAuth
- [ ] Login with GitHub OAuth
- [ ] Enable MFA/TOTP
- [ ] Disable MFA/TOTP
- [ ] Logout
- [ ] Password reset
#### Entity CRUD via Sacred Pipeline (1 hour)
- [ ] Submit new park → appears in moderation queue
- [ ] Submit new ride → appears in moderation queue
- [ ] Submit new company → appears in moderation queue
- [ ] Submit new ride model → appears in moderation queue
- [ ] Moderator approves submission → entity appears
- [ ] Edit existing entity → creates submission → approve → changes apply
- [ ] Delete entity → creates submission → approve → entity deleted
#### Reviews & Social (45 min)
- [ ] Write review → creates submission → approve → appears
- [ ] Mark review helpful
- [ ] Add ride credit → creates submission → approve → appears
- [ ] Create top list
- [ ] Add items to top list
- [ ] Reorder top list items
- [ ] Delete top list
#### Media (30 min)
- [ ] Upload photo to park
- [ ] Upload photo to ride
- [ ] Edit photo caption
- [ ] Delete photo
- [ ] Verify CloudFlare URLs
#### Search (30 min)
- [ ] Global search works
- [ ] Park search with filters works
- [ ] Ride search with filters works
- [ ] Company search works
- [ ] Autocomplete suggests
#### Moderation (30 min)
- [ ] View moderation queue
- [ ] Claim submission
- [ ] Lock extends automatically
- [ ] Approve submission
- [ ] Reject submission
- [ ] Selective approve (approve some items, reject others)
- [ ] Lock releases on navigation
#### Admin (30 min)
- [ ] View admin dashboard
- [ ] View moderation stats
- [ ] View pipeline health
- [ ] Manage reports
- [ ] Manage contact submissions
#### Other Features (30 min)
- [ ] Contact form submission
- [ ] Timeline events display
- [ ] Entity history works
- [ ] Version diffs work
- [ ] Profile editing works
- [ ] Settings pages work
---
### Task 14.6: Next.js Specific Testing (2 hours)
#### Server-Side Rendering (SSR)
- [ ] Pages render on server
- [ ] Data fetching works server-side
- [ ] Metadata appears in HTML source
- [ ] No hydration errors
#### Client-Side Navigation
- [ ] App Router navigation works
- [ ] Parallel routes work (if used)
- [ ] Intercepting routes work (if used)
- [ ] Loading states display correctly
#### Performance
- [ ] Turbopack hot reload is fast
- [ ] Production build is optimized
- [ ] Bundle sizes are acceptable
- [ ] Core Web Vitals meet targets:
- [ ] LCP < 2.5s
- [ ] FID < 100ms
- [ ] CLS < 0.1
#### Environment Variables
- [ ] Server-side env vars work
- [ ] Client-side env vars work (NEXT_PUBLIC_*)
- [ ] No secrets exposed to client
---
### Task 14.7: Sacred Pipeline Verification (1 hour)
**CRITICAL:** Verify pipeline integrity
- [ ] Can submit new park → goes to queue
- [ ] Can submit new ride → goes to queue
- [ ] Can submit new company → goes to queue
- [ ] Can edit park → goes to queue
- [ ] Can edit ride → goes to queue
- [ ] Can edit company → goes to queue
- [ ] Can delete park → goes to queue
- [ ] Can delete ride → goes to queue
- [ ] Can delete company → goes to queue
- [ ] Approval creates entity/applies changes
- [ ] Rejection saves reason
- [ ] No way to bypass moderation
- [ ] All changes create versions (pghistory)
---
### Task 14.8: Browser Console Check (30 min)
Open dev tools in browser and check:
- [ ] No errors in console
- [ ] No failed API requests (check Network tab)
- [ ] No 404s
- [ ] No CORS errors
- [ ] No authentication errors
- [ ] All API calls go to Django (not Supabase)
---
### Task 14.9: Final Documentation Update (30 min)
- [ ] Update migration README with completion status
- [ ] Document any known issues
- [ ] Document deployment notes
- [ ] Update main project README if needed
---
## 🎯 Final Success Criteria
### Code Cleanliness
- [ ] Zero Supabase dependencies in code
- [ ] Zero Supabase dependencies in package.json
- [ ] All imports resolve
- [ ] All TypeScript errors fixed
- [ ] Build succeeds without errors
### Functionality
- [ ] Authentication works (all flows)
- [ ] All entity CRUD works via Sacred Pipeline
- [ ] Reviews work
- [ ] Ride credits work
- [ ] Top lists work
- [ ] Photos work
- [ ] Search works
- [ ] Moderation works
- [ ] Admin functions work
- [ ] Contact form works
- [ ] Timeline works
- [ ] History works
### Sacred Pipeline
- [ ] ALL entity changes go through ContentSubmission
- [ ] NO direct database writes possible
- [ ] Moderation queue functional
- [ ] Approval creates versions
- [ ] Rejection saves reason
- [ ] No bypass mechanisms exist
### Performance
- [ ] Pages load quickly
- [ ] Search is responsive
- [ ] No memory leaks
- [ ] API requests are reasonable
---
## 🚀 Deployment
Once ALL success criteria are met:
### 1. Final Commit
```bash
git add .
git commit -m "Complete Supabase→Django + React→Next.js 15 migration - All phases complete"
git push
```
### 2. Deploy to Staging
```bash
# Deploy Django backend to staging
# Deploy frontend to staging (Vercel/etc)
```
### 3. Staging Verification (2-4 hours)
- [ ] Run ALL integration tests again in staging
- [ ] Monitor error logs
- [ ] Check API response times
- [ ] Verify Sacred Pipeline in staging
### 4. Production Deployment
**Only after staging is 100% verified:**
```bash
# Deploy Django backend to production
# Deploy frontend to production
```
### 5. Post-Deployment Monitoring (24 hours)
- [ ] Watch error logs closely
- [ ] Monitor API response times
- [ ] Check Sacred Pipeline is working
- [ ] Verify no Supabase errors
- [ ] Monitor user feedback
---
## 📝 Rollback Plan
If critical issues are found:
1. Immediately rollback frontend deployment
2. Rollback backend deployment if needed
3. Investigate issue
4. Fix in development
5. Re-test thoroughly
6. Re-deploy
---
## 🎉 MIGRATION COMPLETE!
Once Phase 14 is finished and verified:
**✅ Supabase is 100% removed.**
**✅ ThrillWiki runs on Django REST API.**
**✅ Frontend is Next.js 15 + App Router + Turbopack.**
**✅ Sacred Pipeline remains intact.**
**✅ All features work.**
**✅ Package manager is Bun.**
**✅ Environment variables properly configured.**
Celebrate, but monitor closely for 24-48 hours post-deployment.
---
## 🔗 Related Files
- `migration/README.md` - Overview
- `COMPLETE_SUPABASE_REMOVAL_AUDIT_AND_PLAN.md` - Original audit
- All phase documents (01-12) - Implementation guides
---
**Document Version:** 1.0
**Last Updated:** November 9, 2025

202
migration/README.md Normal file
View File

@@ -0,0 +1,202 @@
# ThrillWiki: Supabase → Django + React → Next.js 15 Migration Plan
## 📊 Current State
**Date:** January 2025
**Django Backend:** ✅ 100% Complete
**Frontend Framework:** React SPA (Vite) → Migrating to Next.js 15 + App Router + Turbopack
**Frontend Services:** ❌ 5% Complete (only reports)
**Frontend Integration:** ❌ 0% Complete (171 files using Supabase)
**Package Manager:** Bun
## 🎯 Migration Goal
This is a **DUAL MIGRATION**:
1. **Backend Service Layer**: Replace ALL Supabase calls with Django REST API service layer
2. **Frontend Framework**: Convert React SPA to Next.js 15 + App Router + Turbopack
**Requirements:**
- ✅ 100% feature parity
- ✅ Sacred Pipeline protection (NEVER bypass moderation)
- ✅ Environment variables (NO hardcoded URLs/keys)
- ✅ Bun for package management
- ✅ All existing URLs preserved
## ⏱️ Total Estimated Time: 180-240 hours
---
## 📋 Phase Overview
| Phase | Name | Time | Priority | Status |
|-------|------|------|----------|--------|
| 1 | Foundation + Next.js Setup | 20-25h | CRITICAL | ⬜ Not Started |
| 2 | Authentication | 18-22h | CRITICAL | ⬜ Not Started |
| 3 | Sacred Pipeline | 20-25h | CRITICAL | ⬜ Not Started |
| 4 | Entity Services | 25-30h | HIGH | ⬜ Not Started |
| 5 | Reviews & Social | 12-15h | HIGH | ⬜ Not Started |
| 6 | Moderation & Admin | 10-12h | HIGH | ⬜ Not Started |
| 7 | Media & Photos | 8-10h | MEDIUM | ⬜ Not Started |
| 8 | Search & Discovery | 8-10h | MEDIUM | ⬜ Not Started |
| 9 | Timeline & History | 6-8h | MEDIUM | ⬜ Not Started |
| 10 | Users & Profiles | 8-10h | MEDIUM | ⬜ Not Started |
| 11 | Contact & Misc | 6-8h | LOW | ⬜ Not Started |
| 12 | Next.js Pages Migration | 45-55h | HIGH | ⬜ Not Started |
| 13 | Next.js Optimization | 15-20h | HIGH | ⬜ Not Started |
| 14 | Cleanup & Testing | 15-18h | CRITICAL | ⬜ Not Started |
---
## 🚨 Critical Path
**You MUST complete phases in this order:**
1. **Phase 1** (Foundation) - Everything depends on this
2. **Phase 2** (Authentication) - Most features require auth
3. **Phase 3** (Sacred Pipeline) - The heart of ThrillWiki
4. **Phase 4** (Entity Services) - Required for Phase 12
After Phase 4, you can work on Phases 5-11 in parallel or any order.
**Phase 12** must wait for Phases 1-4 to be complete.
**Phase 13** must be last.
---
## 📚 Phase Documents
Each phase has a dedicated document with detailed checklists:
- [Phase 1: Foundation](./PHASE_01_FOUNDATION.md)
- [Phase 2: Authentication](./PHASE_02_AUTHENTICATION.md)
- [Phase 3: Sacred Pipeline](./PHASE_03_SACRED_PIPELINE.md)
- [Phase 4: Entity Services](./PHASE_04_ENTITY_SERVICES.md)
- [Phase 5: Reviews & Social](./PHASE_05_REVIEWS_SOCIAL.md)
- [Phase 6: Moderation & Admin](./PHASE_06_MODERATION_ADMIN.md)
- [Phase 7: Media & Photos](./PHASE_07_MEDIA_PHOTOS.md)
- [Phase 8: Search & Discovery](./PHASE_08_SEARCH_DISCOVERY.md)
- [Phase 9: Timeline & History](./PHASE_09_TIMELINE_HISTORY.md)
- [Phase 10: Users & Profiles](./PHASE_10_USERS_PROFILES.md)
- [Phase 11: Contact & Misc](./PHASE_11_CONTACT_MISC.md)
- [Phase 12: Next.js Pages Migration](./PHASE_12_PAGES_MIGRATION.md)
- [Phase 13: Next.js Optimization](./PHASE_13_NEXTJS_OPTIMIZATION.md)
- [Phase 14: Cleanup & Testing](./PHASE_14_CLEANUP_TESTING.md)
### Additional Guides
- [Next.js 15 Migration Guide](./NEXTJS_15_MIGRATION_GUIDE.md)
- [Environment Variables Guide](./ENVIRONMENT_VARIABLES.md)
- [Bun Setup & Usage](./BUN_GUIDE.md)
---
## 🎯 Success Criteria
### Code Quality
- [ ] Zero `import ... from '@supabase/supabase-js'` in codebase
- [ ] Zero `supabase.` method calls
- [ ] All TypeScript errors resolved
- [ ] All linter warnings addressed
- [ ] Test coverage >70% for services
### Functionality
- [ ] All authenticated features work
- [ ] Sacred Pipeline intact: Form → Submission → Moderation → Approval
- [ ] All entity CRUD operations work
- [ ] Search functional
- [ ] Reviews functional
- [ ] Photos functional
- [ ] Moderation queue functional
- [ ] No console errors
### Performance
- [ ] Page load times <2s
- [ ] API response times <500ms
- [ ] No memory leaks
- [ ] Proper caching
---
## 🚨 Critical Rules
1. **Sacred Pipeline is SACRED** - Never bypass moderation
2. **Test as you go** - Don't build everything then test
3. **Keep Supabase temporarily** - Only remove in Phase 13
4. **Commit frequently** - Small, atomic commits
5. **Document changes** - Update docs as you go
6. **No hallucinations** - Only fix REAL problems
7. **Keep it simple** - No feature bloat
---
## 📊 Progress Tracking
Update this section as you complete phases:
**Started:** [Date]
**Current Phase:** Phase X
**Completed Phases:** 0/14
**Estimated Completion:** [Date]
---
## 🔗 Related Documentation
- Django API Documentation: `django/API_GUIDE.md`
- Django Admin Guide: `django/ADMIN_GUIDE.md`
- Sacred Pipeline Audit: `django/SACRED_PIPELINE_AUDIT_AND_IMPLEMENTATION_PLAN.md`
- Complete Migration Audit: `django/COMPLETE_MIGRATION_AUDIT.md`
---
## ⚠️ Important Notes
- **Django Backend is Ready** - All APIs exist and work
- **171 Files Need Migration** - From Supabase to Django service layer
- **Framework Migration** - React SPA → Next.js 15 App Router
- **Package Manager** - Use Bun (not npm/yarn)
- **Environment Variables** - NO hardcoded URLs or keys
- **entitySubmissionHelpers.ts** - 1,200+ lines, most critical file
- **Sacred Pipeline** - NEVER bypass moderation (except moderators)
- **URL Preservation** - All existing URLs must work identically
- **No Data Loss** - All functionality must be preserved
- **No Feature Loss** - 100% feature parity required
## 🌍 Environment Variables
All configuration must use environment variables:
```bash
# .env.local (Next.js)
NEXT_PUBLIC_DJANGO_API_URL=https://api.thrillwiki.com
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=xxx
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID=xxx
CLOUDFLARE_API_TOKEN=xxx
```
See [ENVIRONMENT_VARIABLES.md](./ENVIRONMENT_VARIABLES.md) for complete guide.
## 📦 Bun Package Manager
All npm commands are replaced with Bun:
```bash
bun install # Install dependencies
bun run dev # Start development server
bun run build # Production build
bun test # Run tests
bun add package-name # Add dependency
```
See [BUN_GUIDE.md](./BUN_GUIDE.md) for complete guide.
---
## 🆘 Need Help?
If stuck on a phase:
1. Review the phase document thoroughly
2. Check Django API endpoints in `django/api/v1/endpoints/`
3. Review existing services in `src/services/reports/` as example
4. Test with Postman/curl against Django API
5. Don't proceed until current phase is solid