Files
novi-lessons/v2/Les03-Cursor-Basics/Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md
2026-02-27 13:56:19 +01:00

639 lines
15 KiB
Markdown

# DevDash - Super Hard Debug Challenge (16-18 Errors)
## Challenge Overview
This is the **super hard tier** debug challenge with 16-18 intentional errors distributed across 6 categories. Students must identify and fix all errors to make the project run successfully.
---
## CATEGORY 1: BLOCKING ERRORS (3 Errors)
### Error 1: Missing Dependency - `date-fns`
**File:** `src/lib/utils.ts`
**Issue:** The code imports `format` from `date-fns`:
```typescript
import { format } from "date-fns";
```
But `date-fns` is **NOT** listed in `package.json`.
**Symptom:** Runtime error: "Cannot find module 'date-fns'"
**Fix:** Add to `package.json` dependencies:
```json
"dependencies": {
"date-fns": "^2.30.0"
}
```
---
### Error 2: Wrong Import Path - `next/navigations` (with 's')
**File:** `src/components/Header.tsx`
**Issue:**
```typescript
import { usePathname } from "next/navigations"; // WRONG - has 's'
```
Should be:
```typescript
import { usePathname } from "next/navigation"; // CORRECT
```
**Symptom:** Module not found error at build time
**Fix:** Remove the 's' from the import path.
---
### Error 3: Circular Dependency Crash
**Files:** `src/lib/types.ts` and `src/lib/utils.ts`
**Issue:**
In `src/lib/utils.ts`:
```typescript
import { formatDate } from "./utils"; // Value import, not type
```
In `src/lib/types.ts`:
```typescript
import { formatDate } from "./utils";
export function createPost(data: Omit<Post, "formattedDate">): Post {
return { ...data, formattedDate: formatDate(data.date) }; // formatDate is undefined!
}
```
**Symptom:** `TypeError: formatDate is not a function` at runtime
**Fix:** Use `import type` for types to avoid the circular dependency:
In `src/lib/utils.ts`:
```typescript
import type { Post } from "./types"; // Change to type import
```
Alternatively, restructure to avoid circular imports altogether.
---
## CATEGORY 2: NEXT.JS SPECIFIC ERRORS (4 Errors)
### Error 4: Missing "use client" Directive
**File:** `src/components/ThemeToggle.tsx`
**Issue:** The component uses `useState` (a client-side hook) but **lacks** `"use client"` at the top.
```typescript
// MISSING "use client" at the top!
import { useState } from 'react';
export function ThemeToggle() {
const [isHovered, setIsHovered] = useState(false);
// ...
}
```
**Symptom:** Next.js error: "You're importing a component that needs useState. It only works in Client Components."
**Fix:** Add at the very top of the file:
```typescript
"use client";
import { useState } from 'react';
```
---
### Error 5: Hooks in Server Component
**File:** `src/app/dashboard/page.tsx`
**Issue:** The page uses `useState` and `useEffect` directly:
```typescript
// MISSING "use client" at the top!
import { useState, useEffect } from 'react';
export default function DashboardPage() {
const [isLoading, setIsLoading] = useState(false);
// ...
}
```
**Symptom:** Hydration error or "You're importing a component that needs useState"
**Fix:** Add `"use client"` directive at the top:
```typescript
"use client";
import { useState, useEffect } from 'react';
```
---
### Error 6: Wrong Middleware Matcher Pattern
**File:** `middleware.ts`
**Issue:**
```typescript
export const config = {
matcher: ['/dashboard/:path'], // WRONG - missing asterisk
};
```
This matches `/dashboard/:path` but NOT `/dashboard/page/subpage`. The sub-routes won't be protected.
Also, the middleware looks for `'auth-token'` cookie but the app doesn't set one:
```typescript
const authToken = request.cookies.get('auth-token')?.value;
```
**Symptom:** Sub-routes of dashboard bypass middleware; authentication doesn't work
**Fix:**
```typescript
export const config = {
matcher: ['/dashboard/:path*'], // Add asterisk for all sub-routes
};
```
And update cookie name to match what's actually used or create a mock auth mechanism.
---
### Error 7: `metadata` Export in Client Component
**File:** `src/app/blog/[slug]/page.tsx`
**Issue:**
```typescript
'use client'; // This is a CLIENT component
export const metadata = { // Can't export metadata from client!
title: 'Blog Post',
description: 'Read the full article',
};
import { useParams } from 'next/navigation';
```
**Symptom:** Build error: "Metadata exports must be in a Server Component"
**Fix (Option 1):** Remove `'use client'` and use function parameters instead of `useParams()`:
```typescript
// Remove "use client"
import { generateMetadata } from 'next';
import type { Metadata } from 'next';
export async function generateMetadata({ params }): Promise<Metadata> {
const post = getPostBySlug(params.slug);
return {
title: post?.title || 'Blog Post',
description: post?.content.substring(0, 100) || '',
};
}
export default function BlogPostPage({ params }: { params: { slug: string } }) {
const post = getPostBySlug(params.slug);
// ...
}
```
**Fix (Option 2):** Keep as client component and remove metadata export:
```typescript
'use client';
// Remove metadata export
import { useParams } from 'next/navigation';
// ... rest of component
```
---
## CATEGORY 3: LOGIC ERRORS (3 Errors)
### Error 8: Wrong Filter Condition
**File:** `src/data/posts.ts`
**Issue:**
```typescript
export function getPublishedPosts(): Post[] {
return posts.filter(p => p.draft); // WRONG - returns DRAFTS!
}
```
Should check for `!p.draft` (NOT drafted):
```typescript
export function getPublishedPosts(): Post[] {
return posts.filter(p => !p.draft); // CORRECT
}
```
**Symptom:** Blog page shows only draft posts (only 1 post visible instead of 2 published ones)
**Fix:** Change condition to `!p.draft`.
---
### Error 9: Stale Closure in Event Handler
**File:** `src/components/Testimonials.tsx`
**Issue:**
```typescript
useEffect(() => {
const timer = setInterval(() => {
setCurrentIndex(currentIndex + 1); // STALE CLOSURE!
}, 4000);
return () => clearInterval(timer);
}, []); // Empty dependency array captures initial value
```
The `currentIndex` is captured from the initial render (0). The interval always increments from 0 to 1, causing the carousel to jump back repeatedly.
**Symptom:** Testimonial carousel doesn't advance smoothly; always jumps to position 1
**Fix:** Use the functional update form of `setState`:
```typescript
useEffect(() => {
const timer = setInterval(() => {
setCurrentIndex(prev => (prev + 1) % testimonials.length); // Use prev!
}, 4000);
return () => clearInterval(timer);
}, []);
```
---
### Error 10: Using Index as Key
**File:** `src/components/Features.tsx`
**Issue:**
```typescript
{displayedFeatures.map((feature, index) => (
<div key={index}> // WRONG - using index as key
{/* ... */}
</div>
))}
```
Using index as key is problematic if the list reorders or filters.
**Symptom:** Visual glitches when filtering features; component state gets mixed up
**Fix:** Use unique identifier:
```typescript
{displayedFeatures.map((feature) => (
<div key={feature.id}> // Use feature.id
{/* ... */}
</div>
))}
```
---
## CATEGORY 4: TYPESCRIPT ERRORS (2 Errors)
### Error 11: Wrong API Response Type
**File:** `src/app/api/stats/route.ts` and `src/components/StatsGrid.tsx`
**Issue:**
API returns wrapped response:
```typescript
// src/app/api/stats/route.ts
return NextResponse.json({ data: stats }); // Wrapped in { data: ... }
```
But component expects unwrapped array:
```typescript
// src/components/StatsGrid.tsx
const data = await response.json();
setStats(data); // Expects data to be array, but gets { data: [...] }
```
Later:
```typescript
{stats.map((stat, index) => ( // stats is an object, not array!
```
**Symptom:** TypeError: `stats.map is not a function`
**Fix - Option 1:** Unwrap in component:
```typescript
const data = await response.json();
setStats(data.data); // Extract the array
```
**Fix - Option 2:** Change API response:
```typescript
return NextResponse.json(stats); // Return array directly
```
---
### Error 12: Missing Optional Chaining
**File:** `src/components/UserProfile.tsx`
**Issue:**
```typescript
{user.profile.avatar} // profile could be undefined!
```
The API might return a user without a `profile` object:
```typescript
const user: User = {
id: '1',
name: 'John',
email: 'john@example.com',
// profile is undefined!
};
```
**Symptom:** TypeError: `Cannot read property 'avatar' of undefined`
**Fix:** Use optional chaining:
```typescript
{user.profile?.avatar} // Safe access
```
Or add a null check:
```typescript
{user.profile && user.profile.avatar}
```
---
## CATEGORY 5: REACT PATTERN ERRORS (3 Errors)
### Error 13: Missing useEffect Cleanup
**File:** `src/app/dashboard/page.tsx`
**Issue:**
```typescript
useEffect(() => {
window.addEventListener('resize', handleResize);
// Missing cleanup!
// Should have: return () => window.removeEventListener('resize', handleResize);
}, []);
```
**Symptom:** Memory leak; listeners accumulate when component remounts; "Can't perform a React state update on an unmounted component" warnings
**Fix:** Add cleanup function:
```typescript
useEffect(() => {
function handleResize() {
setIsLoading(window.innerWidth < 768);
}
window.addEventListener('resize', handleResize);
// Add cleanup
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
```
---
### Error 14: Incorrect Context Provider Wrapping
**File:** `src/app/layout.tsx`
**Issue:**
```typescript
<body>
<Header /> {/* Header tries to use theme context but provider hasn't wrapped it yet! */}
<ThemeProvider>
<main>
{children}
</main>
<Footer />
</ThemeProvider>
</body>
```
The `Header` component is rendered BEFORE the `ThemeProvider`, so it can't access the theme context.
**Symptom:** Error in Header when trying to use `useTheme()`: "useTheme must be used within ThemeProvider"
**Fix:** Move provider to wrap everything:
```typescript
<body>
<ThemeProvider>
<Header />
<main>
{children}
</main>
<Footer />
</ThemeProvider>
</body>
```
---
### Error 15: Prop Drilling Mistake - Wrong Prop Name
**File:** `src/app/blog/page.tsx` and `src/components/BlogCard.tsx`
**Issue:**
BlogCard expects `post`:
```typescript
// src/components/BlogCard.tsx
export function BlogCard({ post }: { post: Post }) {
return (
<article>
<h3>{post?.title || 'Untitled'}</h3>
{/* ... */}
</article>
);
}
```
But blog page passes `blogPost`:
```typescript
// src/app/blog/page.tsx
{posts.map((post) => (
<BlogCard key={post.slug} blogPost={post} /> // WRONG prop name!
))}
```
**Symptom:** BlogCard displays "Untitled" and "Date unknown"; content is empty
**Fix:** Use correct prop name:
```typescript
{posts.map((post) => (
<BlogCard key={post.slug} post={post} /> // Correct
))}
```
---
## CATEGORY 6: STYLING ERRORS (3 Errors)
### Error 16: Custom CSS Classes Instead of Tailwind
**File:** `src/app/globals.css`
**Issue:** The stylesheet defines custom classes:
```css
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
.card {
background-color: white;
border-radius: 0.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
padding: 1.5rem;
}
.btn-primary {
background-color: #3b82f6;
color: white;
/* ... more styles ... */
}
```
In a Tailwind project, these should be Tailwind classes, not custom CSS.
**Symptom:** Custom classes work but don't align with Tailwind approach; styling is scattered between CSS and inline styles
**Fix:** Replace with Tailwind classes or @apply directives:
```css
@layer components {
.container {
@apply max-w-5xl mx-auto px-4;
}
.card {
@apply bg-white rounded-lg shadow p-6;
}
.btn-primary {
@apply bg-blue-500 text-white px-6 py-3 rounded transition-colors hover:bg-blue-600;
}
}
```
---
### Error 17-18: Inline Styles Instead of Tailwind Classes
**Files:** Multiple components use inline `style={{}}` instead of Tailwind:
- `src/components/Header.tsx` - All styling is inline
- `src/components/Hero.tsx` - All styling is inline
- `src/components/Features.tsx` - All styling is inline
- `src/components/Testimonials.tsx` - All styling is inline
- `src/components/StatsGrid.tsx` - All styling is inline
- `src/components/BlogCard.tsx` - All styling is inline
- `src/components/UserProfile.tsx` - All styling is inline
- And more...
**Example Issue:**
```typescript
<header style={{
backgroundColor: '#ffffff',
borderBottom: '1px solid #e5e7eb',
padding: '1rem 0'
}}>
{/* ... */}
</header>
```
Should be:
```typescript
<header className="bg-white border-b border-gray-200 py-4">
{/* ... */}
</header>
```
**Symptom:** Project uses Tailwind but styling isn't using Tailwind; inconsistent; not maintainable
**Fix:** Convert all inline styles to Tailwind classes. Example conversions:
```
backgroundColor: '#ffffff' → bg-white
borderBottom: '1px solid #e5e7eb' → border-b border-gray-200
padding: '1rem 0' → py-4
fontSize: '1.5rem' → text-2xl
fontWeight: 'bold' → font-bold
display: 'flex' → flex
gap: '1rem' → gap-4
justifyContent: 'center' → justify-center
alignItems: 'center' → items-center
```
---
## Summary Table
| # | Category | Error | File | Severity |
|---|----------|-------|------|----------|
| 1 | Blocking | Missing `date-fns` | `lib/utils.ts` | CRITICAL |
| 2 | Blocking | Wrong import `next/navigations` | `components/Header.tsx` | CRITICAL |
| 3 | Blocking | Circular dependency | `lib/types.ts`, `lib/utils.ts` | CRITICAL |
| 4 | Next.js | Missing "use client" | `components/ThemeToggle.tsx` | CRITICAL |
| 5 | Next.js | Hooks in server component | `app/dashboard/page.tsx` | CRITICAL |
| 6 | Next.js | Wrong matcher pattern | `middleware.ts` | HIGH |
| 7 | Next.js | Metadata in client component | `app/blog/[slug]/page.tsx` | CRITICAL |
| 8 | Logic | Wrong filter condition | `data/posts.ts` | MEDIUM |
| 9 | Logic | Stale closure | `components/Testimonials.tsx` | MEDIUM |
| 10 | Logic | Index as key | `components/Features.tsx` | MEDIUM |
| 11 | TypeScript | API response type mismatch | `api/stats/route.ts`, `components/StatsGrid.tsx` | HIGH |
| 12 | TypeScript | Missing optional chaining | `components/UserProfile.tsx` | MEDIUM |
| 13 | React | Missing useEffect cleanup | `app/dashboard/page.tsx` | HIGH |
| 14 | React | Wrong provider wrapping | `app/layout.tsx` | CRITICAL |
| 15 | React | Wrong prop name | `app/blog/page.tsx`, `components/BlogCard.tsx` | MEDIUM |
| 16 | Styling | Custom CSS classes | `app/globals.css` | LOW |
| 17-18 | Styling | Inline styles (8+ components) | Multiple components | LOW |
---
## Debugging Strategy
1. **Start with blocking errors** (1-3) - These prevent the app from running at all
2. **Fix Next.js errors** (4-7) - These cause build-time failures
3. **Address logic errors** (8-10) - These cause runtime misbehavior
4. **Resolve TypeScript errors** (11-12) - These break specific features
5. **Fix React patterns** (13-15) - These cause memory leaks and state issues
6. **Clean up styling** (16-18) - These improve code quality and maintainability
---
## Testing Checklist
After fixing all errors:
- [ ] App builds without errors: `npm run build`
- [ ] Dev server runs: `npm run dev`
- [ ] Homepage loads with Hero, Features, Testimonials
- [ ] All navigation links work
- [ ] Blog page shows published posts (2 posts, not drafts)
- [ ] Blog post pages load correctly
- [ ] Dashboard page loads with stats and user profile
- [ ] Theme toggle works (if fixed)
- [ ] Carousel auto-advances smoothly
- [ ] No console errors
- [ ] No memory leaks in browser dev tools