fix: debug challenge
This commit is contained in:
322
Les03-Cursor-Basics/Les03-Debug-Challenge-Hard-ANTWOORDEN.md
Normal file
322
Les03-Cursor-Basics/Les03-Debug-Challenge-Hard-ANTWOORDEN.md
Normal file
@@ -0,0 +1,322 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user