323 lines
9.8 KiB
Markdown
323 lines
9.8 KiB
Markdown
# Debug Challenge HARD - Antwoordenblad (ALLEEN VOOR TIM)
|
||
|
||
**⚠️ NIET DELEN MET STUDENTEN**
|
||
|
||
---
|
||
|
||
## Overzicht
|
||
|
||
Dit project is aanzienlijk complexer dan de easy versie. Het bevat:
|
||
- Blog met dynamische routes
|
||
- Dashboard met statistieken
|
||
- API route voor quotes
|
||
- Utility functions
|
||
- TypeScript types
|
||
|
||
**12 fouten verspreid over 4 categorieën:**
|
||
- Blokkerende fouten (compile/runtime)
|
||
- Logische fouten (code draait maar werkt verkeerd)
|
||
- Pattern fouten (React anti-patterns)
|
||
- Styling fouten (inline styles + custom CSS → Tailwind)
|
||
|
||
**Geschatte tijd: 2-3 uur met AI**
|
||
|
||
---
|
||
|
||
## CATEGORIE 1: BLOKKERENDE FOUTEN
|
||
|
||
### Fout 1: Missing dependency — `clsx`
|
||
|
||
**Bestand:** `lib/utils.ts` (regel 2)
|
||
**Symptoom:**
|
||
```
|
||
Module not found: Can't resolve 'clsx'
|
||
```
|
||
|
||
**Oorzaak:** `clsx` wordt geïmporteerd maar staat niet in package.json.
|
||
`formatDate()` wordt overal gebruikt, dus dit blokkeert ALLES.
|
||
|
||
**Fix:**
|
||
```bash
|
||
npm install clsx
|
||
```
|
||
|
||
**Moeilijkheid:** ⭐ (makkelijk, zelfde als easy versie)
|
||
|
||
---
|
||
|
||
### Fout 2: Verkeerde import path — NextResponse
|
||
|
||
**Bestand:** `app/api/quotes/route.ts` (regel 2)
|
||
**Symptoom:**
|
||
```
|
||
Module not found: Can't resolve 'next/servers'
|
||
```
|
||
|
||
**Oorzaak:** `"next/servers"` moet `"next/server"` zijn (zonder s).
|
||
|
||
**Fix:**
|
||
```ts
|
||
// FOUT:
|
||
import { NextResponse } from "next/servers";
|
||
|
||
// CORRECT:
|
||
import { NextResponse } from "next/server";
|
||
```
|
||
|
||
**Moeilijkheid:** ⭐⭐ (subtiel — 1 letter verschil)
|
||
|
||
---
|
||
|
||
## CATEGORIE 2: LOGISCHE FOUTEN
|
||
|
||
### Fout 3: Array.slice() met verkeerde parameters
|
||
|
||
**Bestand:** `components/BlogPreview.tsx` (regel 7)
|
||
**Symptoom:** BlogPreview sectie op homepage toont GEEN posts (lege grid)
|
||
|
||
**Oorzaak:** `blogPosts.slice(-2, 0)` retourneert een lege array.
|
||
`slice(-2, 0)` betekent: start bij index -2 (2e van achteren), stop bij index 0 — dat is leeg!
|
||
|
||
**Fix:**
|
||
```ts
|
||
// FOUT:
|
||
const recentPosts = blogPosts.slice(-2, 0);
|
||
|
||
// CORRECT:
|
||
const recentPosts = blogPosts.slice(0, 2);
|
||
```
|
||
|
||
**Moeilijkheid:** ⭐⭐⭐ (geen error, gewoon lege output — student moet debuggen WAAROM)
|
||
|
||
---
|
||
|
||
### Fout 4: Icon mapping mist "star" entry
|
||
|
||
**Bestand:** `app/dashboard/page.tsx` (regel 25-30)
|
||
**Symptoom:** De "Rating" stat card toont geen icon (undefined rendered als leeg)
|
||
|
||
**Oorzaak:** `getIcon()` functie heeft entries voor "users", "eye", "clock" maar NIET voor "star". De stats array bevat wel `icon: "star"`.
|
||
|
||
**Fix:**
|
||
```ts
|
||
function getIcon(iconName: string) {
|
||
const icons: Record<string, React.ReactNode> = {
|
||
users: <Users size={24} />,
|
||
eye: <Eye size={24} />,
|
||
clock: <Clock size={24} />,
|
||
star: <Star size={24} />, // ← TOEVOEGEN
|
||
};
|
||
return icons[iconName];
|
||
}
|
||
```
|
||
|
||
**Moeilijkheid:** ⭐⭐⭐ (geen crash, subtiel visueel verschil)
|
||
|
||
---
|
||
|
||
### Fout 5: Mobile menu altijd verborgen
|
||
|
||
**Bestand:** `components/Header.tsx` (regel 62 + 73)
|
||
**Symptoom:** Hamburger menu knop is onzichtbaar, en zelfs als je `display: none` verwijdert van de button, is het dropdown menu ALSOOK `display: none`.
|
||
|
||
**Oorzaak:** Twee problemen:
|
||
1. De hamburger button heeft `display: 'none'` → onzichtbaar
|
||
2. Het mobile menu div heeft `display: 'none'` → zelfs als `menuOpen` true is
|
||
|
||
**Fix:** Dit moet volledig naar responsive Tailwind met `md:hidden`/`md:flex`:
|
||
```tsx
|
||
// Button: verberg op desktop, toon op mobiel
|
||
<button className="md:hidden" ...>
|
||
|
||
// Desktop nav: verberg op mobiel
|
||
<nav className="hidden md:flex gap-6" ...>
|
||
|
||
// Mobile menu: toon als flex wanneer open
|
||
{menuOpen && (
|
||
<div className="flex flex-col md:hidden p-3 border-t bg-white">
|
||
```
|
||
|
||
**Moeilijkheid:** ⭐⭐⭐⭐ (vereist begrijpen van responsive design patterns)
|
||
|
||
---
|
||
|
||
## CATEGORIE 3: REACT PATTERN FOUTEN
|
||
|
||
### Fout 6: async functie in useEffect
|
||
|
||
**Bestand:** `app/dashboard/page.tsx` (regel 35-39)
|
||
**Symptoom:** React warning in console:
|
||
```
|
||
Warning: useEffect must not return anything besides a function
|
||
```
|
||
En mogelijke infinite loop.
|
||
|
||
**Oorzaak:** `useEffect(async () => {...})` is een React anti-pattern. async functie retourneert een Promise, maar useEffect verwacht `void` of een cleanup functie.
|
||
|
||
**Fix:**
|
||
```tsx
|
||
// FOUT:
|
||
useEffect(async () => {
|
||
const res = await fetch("/api/quotes");
|
||
...
|
||
}, [quote]);
|
||
|
||
// CORRECT:
|
||
useEffect(() => {
|
||
async function fetchQuote() {
|
||
const res = await fetch("/api/quotes");
|
||
const data = await res.json();
|
||
setQuote(data.text);
|
||
setLoading(false);
|
||
}
|
||
fetchQuote();
|
||
}, []); // ← ook dependency array gefixed (zie Fout 7)
|
||
```
|
||
|
||
**Moeilijkheid:** ⭐⭐⭐⭐ (vereist kennis van React hooks patterns)
|
||
|
||
---
|
||
|
||
### Fout 7: Verkeerde dependency array → infinite loop
|
||
|
||
**Bestand:** `app/dashboard/page.tsx` (regel 39)
|
||
**Symptoom:** Dashboard pagina loopt vast, continue fetch requests in Network tab
|
||
|
||
**Oorzaak:** `[quote]` als dependency betekent: "run opnieuw als `quote` verandert". Maar de effect ZET quote, wat de effect OPNIEUW triggert → infinite loop.
|
||
|
||
**Fix:**
|
||
```tsx
|
||
// FOUT:
|
||
}, [quote]);
|
||
|
||
// CORRECT:
|
||
}, []); // Lege array = run 1x bij mount
|
||
```
|
||
|
||
**Moeilijkheid:** ⭐⭐⭐⭐ (vereist begrip van useEffect dependency arrays)
|
||
|
||
---
|
||
|
||
### Fout 8: Null check mist op quote state
|
||
|
||
**Bestand:** `components/QuoteOfTheDay.tsx` (regel 22)
|
||
**Symptoom:** `TypeError: Cannot read properties of null (reading 'text')`
|
||
|
||
**Oorzaak:** `quote` begint als `null`, maar zodra loading false is, probeert de component `quote.text` te lezen. Als de API fails of traag is, kan dit crashen.
|
||
|
||
**Fix:** Voeg TypeScript type toe en null check:
|
||
```tsx
|
||
// Verbeterde state typing:
|
||
const [quote, setQuote] = useState<{ text: string; author: string } | null>(null);
|
||
|
||
// In de render:
|
||
{!loading && quote && (
|
||
<>
|
||
<blockquote>"{quote.text}"</blockquote>
|
||
<cite>— {quote.author}</cite>
|
||
</>
|
||
)}
|
||
```
|
||
|
||
**Moeilijkheid:** ⭐⭐⭐ (runtime error, maar duidelijke error message)
|
||
|
||
---
|
||
|
||
## CATEGORIE 4: STYLING FOUTEN (Inline Styles → Tailwind)
|
||
|
||
### Fout 9: Custom CSS classes in globals.css
|
||
|
||
**Bestand:** `app/globals.css`
|
||
**Probleem:** `.card`, `.page-container`, `.section-title` zijn custom CSS classes. Deze horen Tailwind utility classes te zijn.
|
||
|
||
**Fix:** Verwijder custom CSS uit globals.css. Vervang in alle componenten:
|
||
- `.card` → `className="p-6 bg-white rounded-xl border border-gray-200 hover:shadow-lg hover:-translate-y-0.5 transition-all"`
|
||
- `.page-container` → `className="max-w-[1200px] mx-auto px-5"`
|
||
- `.section-title` → `className="text-3xl font-bold text-center mb-10 text-gray-900"`
|
||
|
||
**Moeilijkheid:** ⭐⭐⭐ (moet begrijpen waarom Tailwind beter is dan custom CSS)
|
||
|
||
---
|
||
|
||
### Fout 10-12: Inline styles in alle componenten
|
||
|
||
**Bestanden met inline styles:**
|
||
|
||
| # | Bestand | Complexiteit |
|
||
|---|---------|-------------|
|
||
| 10 | `components/Hero.tsx` | Hoog (gradient, positioning) |
|
||
| 11 | `components/Header.tsx` | Hoog (responsive, sticky) |
|
||
| 12 | `components/Footer.tsx` | Medium (grid, links) |
|
||
| - | `components/StatsSection.tsx` | Medium (grid) |
|
||
| - | `components/BlogPreview.tsx` | Medium (grid, cards) |
|
||
| - | `components/QuoteOfTheDay.tsx` | Laag |
|
||
| - | `app/blog/page.tsx` | Medium (grid, tags) |
|
||
| - | `app/blog/[slug]/page.tsx` | Medium (article layout) |
|
||
| - | `app/dashboard/page.tsx` | Hoog (stats grid, cards) |
|
||
|
||
**Studenten moeten minimaal 3-4 bestanden omzetten.**
|
||
|
||
**Aanpak:**
|
||
1. Selecteer inline style code
|
||
2. `Ctrl+K` → "Converteer inline styles naar Tailwind CSS. Gebruik de brand- kleuren uit tailwind.config.ts waar mogelijk."
|
||
3. Review + Accept
|
||
|
||
**Moeilijkheid:** ⭐⭐ per bestand (maar het zijn er VEEL)
|
||
|
||
---
|
||
|
||
## Samenvatting Alle Fouten
|
||
|
||
| # | Type | Bestand | Ernst | Moeilijkheid |
|
||
|---|------|---------|-------|-------------|
|
||
| 1 | Missing dependency (clsx) | lib/utils.ts | 🔴 Blokkerend | ⭐ |
|
||
| 2 | Verkeerde import (next/servers) | api/quotes/route.ts | 🔴 Blokkerend | ⭐⭐ |
|
||
| 3 | Verkeerde slice() params | BlogPreview.tsx | 🟡 Logisch | ⭐⭐⭐ |
|
||
| 4 | Missende icon mapping | dashboard/page.tsx | 🟡 Logisch | ⭐⭐⭐ |
|
||
| 5 | Mobile menu altijd hidden | Header.tsx | 🟡 Logisch | ⭐⭐⭐⭐ |
|
||
| 6 | async in useEffect | dashboard/page.tsx | 🟠 Anti-pattern | ⭐⭐⭐⭐ |
|
||
| 7 | Infinite loop dep array | dashboard/page.tsx | 🔴 Runtime crash | ⭐⭐⭐⭐ |
|
||
| 8 | Null check quote state | QuoteOfTheDay.tsx | 🔴 Runtime crash | ⭐⭐⭐ |
|
||
| 9 | Custom CSS → Tailwind | globals.css | 🟡 Styling | ⭐⭐⭐ |
|
||
| 10-12 | Inline styles (9+ bestanden) | Overal | 🟡 Styling | ⭐⭐ elk |
|
||
|
||
---
|
||
|
||
## Verwachte Oplos-Volgorde
|
||
|
||
1. `npm install` → werkt
|
||
2. `npm run dev` → `Can't resolve 'clsx'` → `npm install clsx`
|
||
3. `npm run dev` → `Can't resolve 'next/servers'` → fix import
|
||
4. Site laadt! Maar:
|
||
- BlogPreview is leeg → fix slice()
|
||
- Dashboard crasht/loopt vast → fix useEffect + dep array
|
||
- Quote component crasht → fix null check
|
||
- Rating icon mist → fix icon mapping
|
||
- Mobile menu werkt niet → fix responsive
|
||
5. Inline styles → Tailwind conversie
|
||
6. Custom CSS → Tailwind refactor
|
||
|
||
---
|
||
|
||
## Verwacht Request Gebruik
|
||
|
||
| Actie | Requests |
|
||
|-------|----------|
|
||
| Debug blokkerende errors (Chat) | 2-3 |
|
||
| Fix logische fouten (Chat + Edit) | 3-4 |
|
||
| Fix React patterns (Chat) | 2-3 |
|
||
| Inline styles → Tailwind (Inline Edit × 4-5) | 4-5 |
|
||
| **Totaal** | **11-15 requests** |
|
||
|
||
⚠️ Dit is meer dan de easy versie. Studenten op Hobby plan moeten efficiënt werken. Student plan (500/maand) is ruim voldoende.
|
||
|
||
---
|
||
|
||
## Tips voor Tim
|
||
|
||
- **Dit is echt moeilijk.** Fout 5, 6, 7 zijn lastig voor beginners. Verwacht dat studenten hier hulp bij nodig hebben.
|
||
- **De useEffect fouten** zijn een goed leermoment — dit is een HEEL veelvoorkomende bug in React.
|
||
- **De slice() bug** is een subtiele logische fout. Studenten moeten leren: "het werkt zonder errors, maar het resultaat klopt niet" → debuggen.
|
||
- **Mobile menu** is bewust moeilijk. Dit vereist dat ze responsive design begrijpen. Goede Cursor prompt: "Maak deze header responsive met een hamburger menu dat werkt op mobiel met Tailwind."
|
||
- **Geef de easy zip als standaard.** Hard is voor studenten die de easy versie snel af hebben of die extra uitdaging willen.
|