mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 10:31:13 -05:00
725 lines
18 KiB
Markdown
725 lines
18 KiB
Markdown
# 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
|