This guide explains how to add new pages to the QuizMaster application, especially focusing on implementing the missing pages identified in the missing-pages.md document.
QuizMaster uses Next.js App Router, where:
- Each folder under
/apprepresents a route segment page.tsxfiles inside these folders define the UI for that route- Dynamic routes use brackets in folder names like
[id] - API endpoints are defined in the
/app/apidirectory withroute.tsfiles
First, determine where in the routing structure your new page should go:
- User-facing pages go directly under
/app(like/app/quizzes) - Admin pages go under
/app/admin/ - Dynamic pages use folder names with brackets (like
/app/quizzes/[id])
For example, to create the missing /quizzes page:
- Create a directory:
/app/quizzes/page.tsx - For dynamic routes (like
/admin/users/[id]):- Create directory:
/app/admin/users/[id]/ - Add file:
/app/admin/users/[id]/page.tsx
- Create directory:
Here's a template for a new page:
"use client" // Add this if using client-side features like hooks
import { useState, useEffect } from "react"
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
// Define types/interfaces as needed
interface YourDataType {
id: string;
title: string;
// other properties
}
export default function YourPageName() {
// State management, if needed
const [data, setData] = useState<YourDataType[]>([])
const [loading, setLoading] = useState(true)
// Data fetching, if needed
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true)
// Fetch data here - example:
const response = await fetch('/api/your-endpoint')
const result = await response.json()
setData(result)
} catch (error) {
console.error('Error fetching data:', error)
} finally {
setLoading(false)
}
}
fetchData()
}, [])
// UI Rendering
return (
<div className="flex min-h-screen flex-col">
{/* Header - Copy from similar pages for consistency */}
<header className="border-b">
<div className="container flex h-16 items-center justify-between py-4">
<div className="flex items-center gap-2">
<Link href="/" className="font-bold">
QuizMaster
</Link>
</div>
<nav className="flex items-center gap-4">
{/* Add navigation links */}
<Link href="/dashboard" className="text-sm font-medium hover:underline">
Dashboard
</Link>
</nav>
</div>
</header>
{/* Main content */}
<main className="flex-1 container py-6">
<h1 className="text-2xl font-bold mb-6">Your Page Title</h1>
{loading ? (
<p>Loading...</p>
) : (
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{/* Your content goes here */}
{data.map(item => (
<Card key={item.id} className="overflow-hidden">
<CardHeader>
<CardTitle>{item.title}</CardTitle>
</CardHeader>
<CardContent>
{/* Card content */}
<Button className="mt-4">Action Button</Button>
</CardContent>
</Card>
))}
</div>
)}
</main>
</div>
)
}Here's how to implement the missing /quizzes page that shows all available quizzes:
- Create a file at
/app/quizzes/page.tsx - Use this skeleton:
"use client"
import { useState, useEffect } from "react"
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
// Define the quiz type based on the API response
interface Quiz {
id: string;
title: string;
description: string;
difficulty: string;
totalQuestions: number;
metadata: {
totalPoints: number;
averageRating: number;
timesPlayed: number;
};
}
export default function QuizzesPage() {
const [quizzes, setQuizzes] = useState<Quiz[]>([])
const [loading, setLoading] = useState(true)
useEffect(() => {
// Fetch quizzes from the API
const fetchQuizzes = async () => {
try {
setLoading(true)
const response = await fetch('/api/quizzes')
if (!response.ok) {
throw new Error('Failed to fetch quizzes')
}
const data = await response.json()
setQuizzes(data)
} catch (error) {
console.error('Error fetching quizzes:', error)
} finally {
setLoading(false)
}
}
fetchQuizzes()
}, [])
return (
<div className="flex min-h-screen flex-col">
<header className="border-b">
<div className="container flex h-16 items-center justify-between py-4">
<div className="flex items-center gap-2">
<Link href="/" className="font-bold">
QuizMaster
</Link>
</div>
<nav className="flex items-center gap-4">
<Link href="/dashboard" className="text-sm font-medium hover:underline">
Dashboard
</Link>
<Link href="/profile" className="text-sm font-medium hover:underline">
Profile
</Link>
</nav>
</div>
</header>
<main className="flex-1 container py-6">
<h1 className="text-2xl font-bold mb-6">All Quizzes</h1>
{loading ? (
<div className="flex justify-center items-center h-64">
<p>Loading quizzes...</p>
</div>
) : (
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{quizzes.map(quiz => (
<Card key={quiz.id}>
<CardHeader>
<CardTitle>{quiz.title}</CardTitle>
<CardDescription>{quiz.description}</CardDescription>
</CardHeader>
<CardContent>
<div className="flex justify-between text-sm mb-4">
<div>Difficulty: {quiz.difficulty}</div>
<div>{quiz.totalQuestions} questions</div>
</div>
<Button className="w-full" asChild>
<Link href={`/quizzes/${quiz.id}`}>Start Quiz</Link>
</Button>
</CardContent>
</Card>
))}
</div>
)}
</main>
</div>
)
}If your new page needs new API endpoints, follow these steps:
-
Create a new file in the appropriate location under
/app/api- For example:
/app/api/your-feature/route.ts
- For example:
-
Implement the API handler:
import { NextResponse } from 'next/server'
import { promises as fs } from 'fs'
import path from 'path'
import 'server-only'
export async function GET() {
try {
// Your API logic here
// Example: Reading from data files
const dataPath = path.join(process.cwd(), 'data', 'your-data.json')
const fileContents = await fs.readFile(dataPath, 'utf-8')
const data = JSON.parse(fileContents)
return NextResponse.json(data)
} catch (error) {
console.error('Error in API route:', error)
return NextResponse.json(
{ error: "An error occurred" },
{ status: 500 }
)
}
}-
Consistent UI Components
- Reuse the header and navigation structure from existing pages
- Use UI components from
/components/ui/for consistent styling
-
Error Handling
- Always include loading states
- Handle API errors gracefully
- Display user-friendly error messages
-
TypeScript Types
- Define clear interfaces for your data structures
- Reuse types from other files when appropriate
-
Responsive Design
- Use Tailwind's responsive classes (sm:, md:, lg:)
- Test on multiple screen sizes
-
API Integration
- Fetch data from the API endpoints in useEffect hooks
- Handle loading states appropriately
- Consider adding pagination for large data sets
For each missing page identified in missing-pages.md:
-
/quizzes- Implement as shown in the example above
-
/admin/quizzes- Create directory and page.tsx file
- List all quizzes with edit/analytics options
- Provide "Create Quiz" button
-
/admin/quizzes/[id]/edit- Create form for editing quiz details and questions
- Add save functionality
-
/admin/users/*Pages- First create the admin/users directory
- Then implement each specific page with appropriate functionality
- Follow the user management patterns from other admin pages
- Run the development server:
npm run dev - Navigate to your new page route in the browser
- Test all functionality and interactions
- Verify it looks good on different screen sizes
If you need further guidance, refer to:
- The project's existing code for examples
- Next.js App Router Documentation
- The
documentation.mdfile for overall system understanding