fix: add lesson 3

This commit is contained in:
Tim Rijkse
2026-02-27 13:56:19 +01:00
parent 1822546a8e
commit 7d50c78e39
60 changed files with 19865 additions and 1 deletions

View File

@@ -0,0 +1,260 @@
# DevDash Debug Challenge Series - START HERE
Welcome to the **DevDash Debug Challenge Series** - a comprehensive three-tier debugging curriculum for Next.js developers.
## Quick Links
### Debug Challenge Tiers
1. **Easy Tier** (5 errors - 30-45 min)
- File: `les3-debug-challenge.zip`
- Answer Key: `Les03-Debug-Challenge-ANTWOORDEN.md`
- Topics: Basic syntax, typos, simple logic
- **Start here if:** You're new to debugging or Next.js
2. **Hard Tier** (12 errors - 60-90 min)
- File: `les3-debug-challenge-hard.zip`
- Answer Key: `Les03-Debug-Challenge-Hard-ANTWOORDEN.md`
- Topics: Next.js patterns, React hooks, TypeScript
- **Start here if:** You're comfortable with React basics
3. **Super Hard Tier** (16-18 errors - 2-3 hours) ← YOU ARE HERE
- File: `les3-debug-challenge-super-hard.zip`
- Answer Key: `Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md`
- Topics: Advanced patterns, circular deps, complex interactions
- **Start here if:** You're ready to master advanced concepts
## What is DevDash?
DevDash is a fully-featured developer portfolio and dashboard application built with:
- **Next.js 14** (App Router)
- **React 18** (with hooks)
- **TypeScript 5**
- **Tailwind CSS 3**
The application includes:
- Homepage with Hero section, Features grid, Testimonials carousel
- Blog system with dynamic routes
- Dashboard with analytics and user profile
- API routes for data
- Middleware for authentication
- Theme context for dark/light mode
## Getting Started (5 minutes)
### Option 1: Start the Super Hard Challenge
```bash
# 1. Extract the project
unzip les3-debug-challenge-super-hard.zip
cd les3-debug-challenge-super-hard
# 2. Install dependencies
npm install
# 3. Run the development server
npm run dev
# 4. Open browser to http://localhost:3000
# (You'll see errors - that's the point!)
# 5. Read the errors and start debugging
# Open Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md for help
```
### Option 2: Read First, Debug Later
1. Start with `DEBUG-CHALLENGES-OVERVIEW.md` for context
2. Read `Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md` to understand all errors
3. Then extract and debug the project yourself
## What Will You Learn?
By fixing all 16-18 errors, you'll master:
### Advanced React Concepts
- Advanced hook patterns (useState, useEffect, useContext)
- Closure patterns and stale closures
- Key prop best practices
- Memory leak prevention
- Context API provider patterns
### Next.js 14 Deep Dive
- Client vs Server components
- Middleware patterns
- Dynamic routes with metadata
- API routes and type safety
- App Router structure
### TypeScript Mastery
- Type safety in React
- Optional chaining
- API contracts
- Type narrowing
### Advanced Debugging Skills
- Circular dependency detection
- Performance issue identification
- Memory leak detection
- State management debugging
## The 6 Error Categories
| Category | Count | Examples |
|----------|-------|----------|
| **Blocking Errors** | 3 | Missing deps, wrong imports, circular refs |
| **Next.js Specific** | 4 | Client/Server components, metadata, middleware |
| **Logic Errors** | 3 | Filter bugs, stale closures, key issues |
| **TypeScript Errors** | 2 | Type mismatches, optional chaining |
| **React Patterns** | 3 | Cleanup, context, prop drilling |
| **Styling Errors** | 2-3 | Custom CSS, inline styles |
## How to Use the Answer Key
The answer key (`Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md`) provides:
1. **Error Location** - Exact file and line
2. **Issue Description** - What's wrong and why
3. **Symptom** - What breaks or misbehaves
4. **Multiple Solutions** - Usually 2-3 ways to fix
5. **Severity Level** - CRITICAL, HIGH, MEDIUM, LOW
6. **Code Examples** - Before/after comparisons
**Pro Tip:** Try to fix each error without looking at the answer first. Use the answer key to verify your fix or for hints.
## Debugging Strategy
**Recommended approach:**
1. Start the dev server: `npm run dev`
2. Read the console errors
3. Fix blocking errors first (they prevent the app from running)
4. Then tackle Next.js errors (build-time failures)
5. Next, logic errors (functionality breaks)
6. Then TypeScript errors (feature-specific)
7. Finally, React patterns and styling
## Testing Your Fixes
After each fix, verify:
```bash
# Check for build errors
npm run build
# Check for runtime errors
npm run dev
# Run in your browser
# Click around and check if features work
```
## Validation Checklist
Your fixes are complete when:
- [ ] `npm install` succeeds
- [ ] `npm run build` succeeds (no errors)
- [ ] `npm run dev` starts without errors
- [ ] Homepage loads with all sections
- [ ] Blog shows 2 published posts
- [ ] Blog post detail pages work
- [ ] Dashboard page loads
- [ ] Stats load from API
- [ ] No console errors or warnings
- [ ] No TypeScript errors
## Need Help?
### If you're stuck on an error:
1. Read the error message carefully
2. Check `Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md` for that error number
3. Try the "Symptom" section to understand what's broken
4. Look at the code example to see the fix
### If something doesn't make sense:
- Refer to the "Key Concepts Covered" section in the answer key
- Research the specific Next.js/React feature being tested
- Try different solutions to understand the principle
### If you need a broader context:
- Read `DEBUG-CHALLENGES-OVERVIEW.md` for the big picture
- Review the "Learning Outcomes" section
## File Structure
```
les3-debug-challenge-super-hard/
├── src/
│ ├── app/ # Next.js pages and API routes
│ ├── components/ # React components (many with errors)
│ ├── context/ # Theme context
│ ├── lib/ # Utilities and types
│ └── data/ # Static blog data
├── package.json # Dependencies (some missing!)
├── tsconfig.json # TypeScript config
├── tailwind.config.ts # Tailwind setup
├── middleware.ts # Auth simulation (has error)
└── README.md # Project overview
```
## Time Estimate
- **Easy errors (1-3):** 20-30 min
- **Next.js errors (4-7):** 30-40 min
- **Logic errors (8-10):** 25-35 min
- **TypeScript errors (11-12):** 20-30 min
- **React patterns (13-15):** 30-45 min
- **Styling (16-18):** 20-30 min
- **Testing:** 15-20 min
**Total:** 2-3 hours for the complete challenge
## Progression Path
After this challenge, you'll be ready for:
- Building production Next.js applications
- Advanced React patterns
- Complex state management
- Performance optimization
- Team code reviews
## Questions About Specific Errors?
Each error is thoroughly explained in `Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md`. The document includes:
- Exact error location
- Root cause explanation
- Visual code examples
- Multiple solution options
- Why the error matters
- Best practices to avoid it
## Next Steps
1. **Now:** Extract the zip and run `npm install`
2. **Then:** Start the dev server with `npm run dev`
3. **When stuck:** Check the answer key
4. **After fixing all:** Review your solutions and make sure you understand each error
5. **Finally:** Try building a similar project from scratch using these patterns
---
## Document Reference
| Document | Purpose |
|----------|---------|
| **00-START-HERE.md** | This file - quick start guide |
| **DEBUG-CHALLENGES-OVERVIEW.md** | Big picture overview of all 3 tiers |
| **Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md** | Complete answer key with explanations |
| **les3-debug-challenge-super-hard.zip** | The actual buggy project |
---
**Good luck debugging! You've got this.**
Remember: The goal isn't just to fix errors, but to understand *why* they happened and how to prevent them in the future.
---
Created: February 2026
Level: Super Hard (16-18 errors)
Time: 2-3 hours

View File

@@ -0,0 +1,222 @@
# DevDash Debug Challenge Series - Complete Overview
This folder contains a complete series of 3 debug challenges of increasing difficulty, all featuring the "DevDash" developer portfolio and dashboard application.
## Challenge Tiers
### 1. Easy Tier (5 Errors)
- **File:** `les3-debug-challenge.zip`
- **Answer Key:** `Les03-Debug-Challenge-ANTWOORDEN.md`
- **Target:** Beginners, first-time debuggers
- **Topics:** Basic syntax errors, typos, simple logic mistakes
### 2. Hard Tier (12 Errors)
- **File:** `les3-debug-challenge-hard.zip`
- **Answer Key:** `Les03-Debug-Challenge-Hard-ANTWOORDEN.md`
- **Target:** Intermediate developers
- **Topics:** Next.js patterns, React hooks, TypeScript basics, state management
### 3. Super Hard Tier (16-18 Errors)
- **File:** `les3-debug-challenge-super-hard.zip`
- **Answer Key:** `Les03-Debug-Challenge-SuperHard-ANTWOORDEN.md`
- **Target:** Advanced developers, mastery level
- **Topics:** Circular dependencies, advanced React patterns, middleware, complex type issues
## Challenge Structure
All challenges are based on the same application concept: **DevDash** - a developer portfolio and dashboard application built with Next.js 14, React 18, TypeScript, and Tailwind CSS.
### Features Included
- **Homepage:** Hero section, features showcase, testimonials carousel
- **Blog:** Dynamic routes with MDX-style content
- **Dashboard:** Analytics, stats grid, user profile, charts placeholder
- **API Routes:** `/api/stats`, `/api/posts`
- **Middleware:** Authentication simulation
- **Context:** Theme management (dark/light mode)
### Tech Stack
- Next.js 14 (App Router)
- React 18
- TypeScript 5
- Tailwind CSS 3
- PostCSS
## Error Categories by Tier
### Easy (5 errors)
1. Wrong variable name
2. Missing closing tag
3. Typo in function name
4. Array method confusion
5. Logic error in condition
### Hard (12 errors)
- 3 Blocking errors (missing deps, wrong imports)
- 3 Next.js specific errors (layout, metadata, routing)
- 2 Logic errors
- 2 TypeScript errors
- 2 React pattern errors
### Super Hard (16-18 errors)
- 3 Blocking errors (circular dependencies, missing deps)
- 4 Next.js specific errors (client/server components, middleware)
- 3 Logic errors (filters, closures, keys)
- 2 TypeScript errors (type mismatches, optional chaining)
- 3 React pattern errors (cleanup, context, props)
- 2-3 Styling errors (CSS and inline styles)
## Getting Started
Each zip file contains a complete Next.js project. To work on a challenge:
```bash
# Extract the zip
unzip les3-debug-challenge-super-hard.zip
# Navigate to project
cd les3-debug-challenge-super-hard
# Install dependencies
npm install
# Run development server
npm run dev
# Open browser to http://localhost:3000
```
## Using Answer Keys
Each answer key document provides:
- Exact location of each error
- Code snippet showing the problem
- Explanation of why it's an error
- The symptom (what breaks)
- One or more solutions
- Difficulty level (CRITICAL, HIGH, MEDIUM, LOW)
## Progression Path
**Suggested learning path:**
1. Start with **Easy Tier** to get familiar with the codebase and debugging process
2. Move to **Hard Tier** to tackle more complex Next.js and React patterns
3. Finish with **Super Hard Tier** for mastery of advanced debugging concepts
## Timeline Recommendations
| Tier | Recommended Time | Notes |
|------|-----------------|-------|
| Easy | 30-45 minutes | Quick confidence builder |
| Hard | 60-90 minutes | Requires research and testing |
| Super Hard | 120-180 minutes | Deep analysis and multiple fixes needed |
## Learning Outcomes
After completing all three tiers, students will have mastered:
### Easy Tier
- Basic syntax debugging
- Reading error messages
- Variable naming conventions
- Simple conditional logic
### Hard Tier
- Next.js App Router concepts
- React hooks and their rules
- TypeScript type safety basics
- Component composition
- Data flow and props
### Super Hard Tier
- Circular dependencies and module loading
- Advanced React patterns (closures, cleanup, context)
- Client vs Server components
- Middleware patterns
- Type-safe API contracts
- Performance patterns (keys, memoization)
- Styling best practices with Tailwind
## Key Concepts Covered
### JavaScript/TypeScript
- Module imports and circular dependencies
- Type safety and optional chaining
- Closures and their pitfalls
- Array methods and filtering
- Object destructuring
### React
- Hooks (useState, useEffect, useContext)
- Rules of hooks and dependencies
- Client vs Server components
- Context API and providers
- Key props and list rendering
- Memory leaks and cleanup
### Next.js
- App Router structure
- API routes
- Middleware
- Metadata and SEO
- Dynamic routes
- Image optimization
### CSS/Styling
- Tailwind CSS utilities
- Inline styles vs. classes
- CSS custom properties
- Responsive design
## Files Included
Each zip contains:
```
les3-debug-challenge-super-hard/
├── src/
│ ├── app/ # Next.js pages and layouts
│ ├── components/ # React components
│ ├── context/ # React Context
│ ├── lib/ # Utilities and types
│ └── data/ # Static data
├── package.json
├── tsconfig.json
├── tailwind.config.ts
├── next.config.js
├── postcss.config.js
├── middleware.ts
└── README.md
```
## Grading Rubric
Students can self-assess:
- All 5/12/16-18 errors identified and documented
- Code builds successfully (`npm run build`)
- No runtime errors or warnings
- Application functionality works as intended
- Code follows best practices (Tailwind usage, React patterns, etc.)
- Understanding demonstrated in commit messages or documentation
## Support for Instructors
Each answer key provides:
- Clear explanation of each error
- Multiple solution options where applicable
- Severity levels to help prioritize feedback
- Testing checklist to verify all fixes
- Common student mistakes and misconceptions
## File Sizes
| File | Size | Components |
|------|------|-----------|
| les3-debug-challenge.zip | 12 KB | 10 files |
| les3-debug-challenge-hard.zip | 19 KB | 32 files |
| les3-debug-challenge-super-hard.zip | 26 KB | 38 files |
---
**Created:** February 2026
**Updated:** February 25, 2026
**Version:** 3.0 (Complete Series)

View File

@@ -0,0 +1,493 @@
# Les 3: Cursor Basics - Lesopdracht (In-Class Assignment)
## Doelstelling
In deze les leer je hoe je **Cursor** effectief gebruikt om snel professionele componenten te bouwen. Je maakt een **NIEUW** Next.js project met Cursor Composer, genereert AI-gegenereerde cursorrules, en oefent met Tab Completion, Chat, Inline Edit en Composer.
**Eindresultaat:** Een werkende Next.js applicatie met Hero component en Feature Cards, volledig gebouwd met Cursor.
---
## Timing
**Totaal: 85 minuten**
- Setup & Git: 10 min
- Skills & cursorrules: 10 min
- Hero Component (Composer): 20 min
- Feature Cards (Composer): 20 min
- Inline Edit styling: 15 min
- Git commit: 5 min
- Buffer: 5 min
---
## Voorbereiding
### Benodigde software
- **Cursor** (gedownload en geïnstalleerd van cursor.com)
- **Node.js** 18+ en npm
- **Git** (voor versiecontrole)
- **Terminal/Command Prompt**
---
## Stap 1: Project aanmaken met Git (10 minuten)
### Stap 1.1: Nieuwe folder en Git init
Open Terminal/Command Prompt op je computer (niet in Cursor nog):
```bash
mkdir les3-cursor-project
cd les3-cursor-project
git init
```
### Stap 1.2: Open in Cursor
```bash
cursor .
```
Dit opent je lege project in Cursor.
**Checkpoint 1:** Project is open in Cursor met lege folder.
### Stap 1.3: Create Next.js app in Cursor Terminal
Open de Terminal in Cursor met **Ctrl+`** (backtick):
```bash
npx create-next-app@latest . --typescript --tailwind --app
```
Beantwoord alle vragen met **Yes (y)**:
- TypeScript? → **Yes**
- ESLint? → **Yes**
- Tailwind CSS? → **Yes**
- `src/` directory? → **No**
- App Router? → **Yes**
- Default import alias? → **Yes**
Dit duurt ~2-3 minuten. Wacht tot het klaar is.
**Checkpoint 2:** Next.js project is aangemaakt. Je ziet `app/`, `components/`, `package.json`, etc. in de file tree.
---
## Stap 2: Cursor Skills & Docs (10 minuten)
### Stap 2.1: Open Settings
Klik op het **⚙️ Settings icoon** linksonder in Cursor.
### Stap 2.2: Activeer Docs
Ga naar **Features****Docs**
Voeg deze drie documenten toe (zoek en click Add):
1. **Next.js** (officiële docs)
2. **React** (officiële docs)
3. **Tailwind CSS** (officiële docs)
Dit helpt Cursor veel beter code te genereren omdat het toegang heeft tot de juiste documentatie.
### Stap 2.3: Controleer Tab Completion
Ga naar **Features****Completion**
- Zorg dat **Tab Completion** is ingeschakeld (dit is gratis!)
**Checkpoint 3:** Alle drie docs zijn zichtbaar in je Docs sidebar rechts. Tab Completion is enabled.
---
## Stap 3: Generate cursorrules met Chat (1 request)
### Stap 3.1: Open Cursor Chat
Druk **Ctrl+L** (Windows/Linux) of **Cmd+L** (Mac).
Een chat panel opent aan de zijkant.
### Stap 3.2: Prompt voor .cursorrules
Typ deze prompt en druk Enter:
```
Je bent een expert Next.js developer. Genereer een .cursorrules bestand
voor een Next.js project met TypeScript, Tailwind CSS, en App Router.
Het bestand moet guidelines bevatten voor:
- Functional components en React hooks
- TypeScript best practices
- Tailwind CSS utilities (nooit inline styles)
- Bestandsnaamgeving (PascalCase voor components, camelCase voor utils)
- Folder structuur (/app, /components, /lib)
- Comments in het Nederlands
- Keine `any` types
Output het volledige .cursorrules bestand klaar om te copy-pasten.
```
Wacht op Cursor's antwoord.
### Stap 3.3: Copy de output
Cursor geeft je het complete .cursorrules bestand.
Selecteer alle code (Ctrl+A in de chat output) en kopieer het.
### Stap 3.4: Maak .cursorrules bestand
1. Rechtsklik op de root folder in de left sidebar
2. Klik "New File"
3. Noem het `.cursorrules` (met punt aan het begin!)
4. Plak de content
Save het bestand (Ctrl+S).
**Checkpoint 4:** `.cursorrules` bestand bestaat in je project root met goede guidelines.
**Request Budget: 1 van ~7 used**
---
## Stap 4: Hero Component met Composer (20 minuten)
### Stap 4.1: Maak component file
1. Rechtsklik op de `app` folder
2. "New File"
3. Noem het `app/components/Hero.tsx`
(Dit maakt automatisch de `components` subfolder)
### Stap 4.2: Open Composer
Met het Hero.tsx file open, druk **Ctrl+Shift+I** (Windows/Linux) of **Cmd+Shift+I** (Mac).
De Composer panel opent.
### Stap 4.3: Geef instructie aan Composer
In Composer, typ:
```
Maak een Hero component (TypeScript, React functional component) met:
- Grote h1 heading: "Welkom bij Cursor"
- Subheading met beschrijvende tekst: "Bouw websites snel met AI"
- Een CTA button: "Aan de slag" (link naar #features)
- Responsive design: Tailwind CSS (md: breakpoint)
- Flexbox voor verticale centrering
- Dark mode compatible (dark: classes)
- Gradient background (blauw naar paars)
- Geen external dependencies
Geef alleen de component code, geen imports of exports statements voor buiten.
```
Cursor genereert het component.
### Stap 4.4: Accept of refine
Review de getoonde code. Klik **Accept All** (groene knop) om het in je bestand in te voegen.
**Checkpoint 5:** Hero.tsx file bevat een werkend Hero component met juiste structuur.
**Request Budget: 2 van ~7 used**
---
## Stap 5: Feature Cards Component met Composer (20 minuten)
### Stap 5.1: Maak component file
1. Rechtsklik op `app` folder
2. "New File"
3. Noem het `app/components/FeatureCards.tsx`
### Stap 5.2: Open Composer
Druk **Ctrl+Shift+I** in dit nieuwe bestand.
### Stap 5.3: Composer prompt
```
Maak een FeatureCards component (TypeScript, React) dat:
- Een grid van 3 feature cards toont
- Responsive: 1 kolom op mobile, 3 kolommen op desktop (Tailwind grid)
- Elke card heeft:
* Een icon uit lucide-react (Package, Zap, Layers)
* Een h3 title
* Een description text
- Hover effect: transform scale-105 en shadow toename
- Cards hebben border en padding (Tailwind)
- Dark mode ready
- lucide-react is geïmporteerd bovenaan
Geef alleen de component code.
```
Composer genereert het component.
### Stap 5.4: Accept
Klik **Accept All**.
### Stap 5.5: Install lucide-react
In je Cursor Terminal (Ctrl+`):
```bash
npm install lucide-react
```
Wacht tot npm klaar is.
**Checkpoint 6:** FeatureCards.tsx werkt, lucide-react is geïnstalleerd.
**Request Budget: 3 van ~7 used**
---
## Stap 6: Integreer in Homepage (5 minuten)
### Stap 6.1: Open `app/page.tsx`
Dit is je homepage.
### Stap 6.2: Update de content
Vervang de bestaande content (of voeg toe aan het bestand) met:
```typescript
import Hero from './components/Hero';
import FeatureCards from './components/FeatureCards';
export default function Home() {
return (
<main className="min-h-screen">
<Hero />
<section className="py-20 bg-gray-50 dark:bg-gray-900">
<div className="container mx-auto px-4">
<h2 className="text-4xl font-bold mb-12 text-center">Features</h2>
<FeatureCards />
</div>
</section>
</main>
);
}
```
Save (Ctrl+S).
### Stap 6.3: Test in browser
In Cursor Terminal:
```bash
npm run dev
```
Open `http://localhost:3000` in je browser.
Je ziet nu: Hero → Features section met 3 cards.
**Checkpoint 7:** Website draait lokaal en ziet er netjes uit.
---
## Stap 7: Styling Refinement met Inline Edit (15 minuten)
Nu gebruiken we **Inline Edit** (Ctrl+K) voor kleine styling tweaks.
### Stap 7.1: Hero button styling
1. Open `app/components/Hero.tsx`
2. Klik ergens in de button JSX
3. Druk **Ctrl+K**
4. Type:
```
Voeg een hover effect toe aan de button:
- Scale slightly bigger on hover
- Change background color smoothly
- Add smooth transition
```
5. Review de suggestie, klik Accept
### Stap 7.2: Card shadows verbeteren
1. Open `app/components/FeatureCards.tsx`
2. Selecteer een card div
3. Druk **Ctrl+K**
4. Type:
```
Voeg een mooier shadow effect toe aan de cards:
- Larger shadow on hover
- Smooth transition
- Better depth perception
```
5. Accept
### Stap 7.3: Typography verbeteren
1. Open `app/page.tsx`
2. Druk **Ctrl+K**
3. Type:
```
Maak de "Features" heading groter en mooier:
- Groter font size
- Betere kleur gradient
```
5. Accept
Refresh je browser (F5) om de changes te zien.
**Checkpoint 8:** Styling ziet er gepolijst uit, hover effects werken.
**Request Budget: 4-5 van ~7 used**
---
## Stap 8: Git Commit (5 minuten)
### Stap 8.1: Open Source Control
Druk **Ctrl+Shift+G** in Cursor.
Source Control panel opent links.
### Stap 8.2: Stage alle bestanden
Klik op het **+** icoon bij "Changes" of druk **Ctrl+Shift+A**.
Alle bestanden worden staged (groen).
### Stap 8.3: Write commit message
In het message veld, typ:
```
feat: Build Hero and FeatureCards components with Cursor Composer
```
### Stap 8.4: Commit
Druk **Ctrl+Enter** of klik de ✓ button.
Je commit is gemaakt!
**Checkpoint 9:** Git commit is succesvol (geen errors).
---
## Keyboard Shortcuts Reference
| Actie | Windows | Mac |
|-------|---------|-----|
| **Cursor Chat** | Ctrl+L | Cmd+L |
| **Composer** | Ctrl+Shift+I | Cmd+Shift+I |
| **Inline Edit (quick fix)** | Ctrl+K | Cmd+K |
| **Terminal** | Ctrl+` | Cmd+` |
| **Command Palette** | Ctrl+Shift+P | Cmd+Shift+P |
| **Source Control** | Ctrl+Shift+G | Cmd+Shift+G |
| **Save** | Ctrl+S | Cmd+S |
| **Tab Completion** | Tab | Tab |
---
## Request Budget Tracking
Je hebt **~7 requests** beschikbaar in deze les:
| Fase | Type | Requests |
|------|------|----------|
| cursorrules generatie | Chat | 1 |
| Hero Component | Composer | 1 |
| FeatureCards Component | Composer | 1 |
| Button styling tweak | Inline Edit | 0.5 |
| Card styling tweak | Inline Edit | 0.5 |
| Typography tweak | Inline Edit | 0.5 |
| Buffer/extra | - | 2.5 |
**💡 Pro tip:** Gebruik **Tab Completion** (gratis!) voor:
- Imports toevoegen
- Props schrijven
- Repetitieve code (zelfde classNames herhalen)
---
## Troubleshooting
### Probleem: "Module not found: lucide-react"
**Oplossing:**
```bash
npm install lucide-react
npm run dev
```
(Herstart je dev server na npm install)
### Probleem: Composer stelt rare dingen voor
**Oplossing:** Wees specifieker in je prompt. Verwijs naar bestaande code:
- "Voeg lucide-react icons toe" (voldoende duidelijk)
- "Zorg dat het met TypeScript werkt" (specifieer)
### Probleem: Inline Edit accepteert niet
**Oplossing:** Klik de groene ✓ knop of druk Ctrl+Enter
### Probleem: `npm run dev` geeft error
**Stappen:**
1. Controleer dat je in de juiste folder bent: `pwd`
2. Controleer node versie: `node --version` (moet 18+ zijn)
3. Verwijder en herinstalleer dependencies:
```bash
rm -rf node_modules package-lock.json
npm install
npm run dev
```
### Probleem: Port 3000 al in gebruik
```bash
npm run dev -- -p 3001
# Openen: http://localhost:3001
```
### Probleem: Git werkt niet
**Controleer:**
- Ben je in de juiste folder? `pwd` moet je project folder zijn
- Bestaat `.git` folder? `ls -la .git`
- Zijn je bestanden getracked? `git status`
---
## Checklist - Vinkje alles af voor je klaar bent
- [ ] Git init en create-next-app uitgevoerd
- [ ] Cursor Skills (Next.js, React, Tailwind) geactiveerd
- [ ] `.cursorrules` bestand gegenereerd en in root geplaatst
- [ ] `app/components/Hero.tsx` aangemaakt met Composer
- [ ] `app/components/FeatureCards.tsx` aangemaakt met Composer
- [ ] lucide-react geïnstalleerd via npm
- [ ] Componenten geïmporteerd in `app/page.tsx`
- [ ] `npm run dev` draait zonder errors
- [ ] Website ziet er goed uit op http://localhost:3000
- [ ] Styling verfijnd met Inline Edit (minimaal 2 aanpassingen)
- [ ] Git commit gemaakt met duidelijke message
- [ ] Alle bestanden zijn saved (geen rode stippen in sidebar)
---
## Volgende stappen
**Voor volgende les:**
- Zorg dat `npm run dev` nog werkt
- Download het huiswerk zip bestand van Teams (les3-debug-challenge.zip)
- Voorkom dat je project files verwijdert/opslaanslecht
**Veel succes! 🚀**

View File

@@ -0,0 +1,375 @@
# Les 3: Cursor Basics - Huiswerkopdracht (Homework - DEBUG CHALLENGE)
**Deadline:** Vóór Les 4
**Type:** Debug Challenge - Je krijgt een kapot Next.js project en moet het repareren met Cursor.
---
## Introductie
Dit huiswerk is anders dan gewoon code schrijven. Je krijgt een **intentioneel kapot** Next.js project dat je met Cursor moet debuggen.
Je leert:
- Errors opsporen met Cursor
- De Terminal gebruiken voor error messages
- Inline styles naar Tailwind CSS converteren
- Missing imports en dependencies vinden
- ESLint warnings beheren
**Geschatte tijd:** 1.5-2 uur
---
## Stap 0: Download het Zip-bestand
Download `les3-debug-challenge.zip` van Teams.
Unzip het bestand:
```bash
unzip les3-debug-challenge.zip
cd les3-debug-challenge
```
Open het in Cursor:
```bash
cursor .
```
---
## Wat je tegenkomt
Het project `les3-debug-challenge` bevat deze INTENTIONELE ERRORS:
### Error Type 1: Missing Dependencies
**Wat:** `lucide-react` wordt gebruikt in `components/HeroIcon.tsx`, maar staat niet in `package.json`.
**Hoe te vinden:**
- Open Terminal in Cursor (Ctrl+`)
- Run: `npm run dev`
- Je ziet: `Error: Module not found: lucide-react`
**Hoe te fixen:**
```bash
npm install lucide-react
```
### Error Type 2: Syntax Errors (typos, missing closing tags)
**Wat:** Een component heeft een vergeten closing JSX tag of import typo.
**Bijv. in `app/page.tsx`:**
```tsx
<Hero // Missing closing >
```
Of in imports:
```tsx
import { Button } from '@/componenst/Button' // typo: componenst
```
**Hoe te vinden:**
- Terminal errors: `SyntaxError` of `Cannot find module`
- Cursor geeft rode underlines in editor
- Gebruik Cursor Chat (Ctrl+L) om de error uit te leggen
**Hoe te fixen:**
1. Kijk naar de regel number in error
2. Open dat bestand
3. Controleer imports en JSX syntax
4. Fix typos
5. Save en test: `npm run dev`
### Error Type 3: Inline Styles (moeten naar Tailwind)
**Wat:** Een component gebruikt `style={{...}}` ipv Tailwind classes.
**Bijv. in `components/Card.tsx`:**
```tsx
<div style={{
padding: '20px',
backgroundColor: '#f0f0f0',
borderRadius: '8px',
border: '1px solid #ccc'
}}>
Content
</div>
```
**Hoe te fixen:** Gebruik Cursor **Inline Edit (Ctrl+K)**
1. Open het bestand
2. Selecteer de `style={{...}}` block
3. Druk **Ctrl+K**
4. Type:
```
Converteer deze inline styles naar Tailwind CSS classes.
Verwijder de style prop en voeg className toe.
```
5. Cursor converteerd het naar:
```tsx
<div className="p-5 bg-gray-100 rounded-lg border border-gray-300">
Content
</div>
```
6. Accept en verify
**Tabel: Inline Styles → Tailwind Mapping**
| Inline Style | Tailwind Class |
|--------------|----------------|
| `padding: '20px'` | `p-5` (4px × 5) |
| `backgroundColor: '#f0f0f0'` | `bg-gray-100` |
| `borderRadius: '8px'` | `rounded-lg` |
| `border: '1px solid #ccc'` | `border border-gray-300` |
| `marginBottom: '16px'` | `mb-4` |
| `display: 'flex'` | `flex` |
| `justifyContent: 'center'` | `justify-center` |
### Error Type 4: Missing Imports
**Wat:** Een component wordt gebruikt maar niet geïmporteerd.
**Bijv. in `app/page.tsx`:**
```tsx
export default function Home() {
return <FeatureCards /> // FeatureCards is niet geïmporteerd!
}
```
**Hoe te vinden:**
- Cursor geeft rode underline: "Cannot find name 'FeatureCards'"
- Terminal error bij `npm run dev`
**Hoe te fixen:**
- Voeg import bovenaan toe:
```tsx
import FeatureCards from '@/components/FeatureCards';
```
Of gebruik Cursor's **Quick Fix** (Ctrl+. op de fout).
---
## Debug-aanpak stap-voor-stap
### Stap 1: Installeer eerst alles
```bash
npm install
```
### Stap 2: Start dev server
```bash
npm run dev
```
Lees ALLE errors van boven naar beneden.
### Stap 3: Fix errors één voor één
**Beginnen bij:**
1. **npm install errors** (missing packages)
2. **Syntax errors** (ESLint, typos)
3. **Runtime errors** (module not found, missing imports)
4. **Styling issues** (inline styles)
### Stap 4: Use Cursor Chat voor moeilijke errors
Druk **Ctrl+L** en beschrijf de error:
```
Ik krijg deze error:
Error: Cannot find module 'lucide-react'
Hoe fix ik dit?
```
Cursor helpt je.
### Stap 5: Verify in browser
Na elke fix:
1. Zorg dat `npm run dev` draait zonder errors
2. Open http://localhost:3000
3. Check visueel dat alles werkt
4. Geen rode errors in console (F12 → Console tab)
---
## Checklist van errors om op te lossen
Zorg dat je ALLE deze punten fixt:
- [ ] **npm install errors** - Alle missing dependencies geïnstalleerd
- [ ] **Syntax errors** - Alle closing tags toegevoegd, imports gecorrigeerd
- [ ] **Missing imports** - Alle componenten die gebruikt worden zijn geïmporteerd
- [ ] **Inline styles** - Minimaal 3 inline styles → Tailwind conversions
- [ ] **ESLint warnings** - Geen rode/oranje squiggly lines meer
- [ ] **Browser console** - Geen rood errors (F12)
- [ ] **Visual check** - Website ziet er correct uit
---
## Tips per error-type
### Voor Missing Dependencies
```bash
# Check welke packages ontbreken
npm install
# Of installeer handmatig
npm install [package-name]
```
### Voor Syntax Errors
- **Controleer imports:** `import X from '@/components/X'` (check spelling)
- **Controleer JSX closing:** Elke `<div>` moet `</div>` hebben
- **Controleer quotes:** Tailwind classes moeten in dubbele quotes: `className="..."`
### Voor Inline Styles → Tailwind
Gebruik altijd Inline Edit (Ctrl+K):
1. Select de `style={{...}}` code
2. Ctrl+K
3. Type: "Convert to Tailwind classes"
4. Accept
Tailwind Cheat Sheet:
- Padding: `p-2`, `p-4`, `p-8` (2, 4, 8 × 4px)
- Margin: `m-2`, `mb-4`, `mt-2`
- Background: `bg-white`, `bg-gray-100`, `bg-blue-500`
- Border: `border`, `border-2`, `border-gray-300`
- Radius: `rounded`, `rounded-lg`, `rounded-full`
- Display: `flex`, `grid`, `block`
- Colors: `text-white`, `bg-red-500`, `border-blue-300`
### Voor Missing Imports
Cursor's Quick Fix (Ctrl+.) werkt vaak:
1. Klik op de rode error
2. Druk Ctrl+.
3. "Add import"
Of manual:
```tsx
import ComponentNaam from '@/components/ComponentNaam';
```
---
## Submitting Your Work
### Voor Les 4:
Maak een **GitHub commit** en **push** het:
```bash
git add .
git commit -m "fix: debug all errors and convert styles to Tailwind"
git push
```
Post in Teams:
1. **GitHub link** naar je debugged project
2. **Screenshot** van je werkende website (http://localhost:3000)
3. **Korte beschrijving:** Welke errors heb je gerepareerd?
```
- Fixed missing lucide-react dependency
- Fixed typo in import statement (componenst → components)
- Converted 5 inline styles to Tailwind classes
- Added missing Hero import to page.tsx
- Verified no ESLint errors
```
### Checklist voor inlevering
- [ ] Alle npm/syntax/import errors zijn fixed
- [ ] `npm run dev` draait zonder errors
- [ ] http://localhost:3000 toont de website correct
- [ ] Browser console (F12) toont geen errors
- [ ] Minimaal 3 inline styles zijn converted naar Tailwind
- [ ] Git commit is gemaakt
- [ ] GitHub link gepost in Teams
---
## Bonus: Maak je eigen component (optioneel)
Na alle errors te hebben opgelost, voeg je **een eigen component** toe:
Ideeën:
- Een "Newsletter Signup" sectie
- Een "Call to Action" button bar
- Een "Stats" sectiie met cijfers
- Een "Testimonials" carousel
- Een "FAQ" sectie met accordions
**Hoe:**
1. Open Composer (Ctrl+Shift+I)
2. Prompt:
```
Maak een [ComponentNaam] component met:
- [beschrijving]
- Tailwind CSS styling
- Responsive design
- TypeScript types
Voeg het toe aan /components en importeer het in /app/page.tsx
```
3. Accept en test
**Bonus punten!**
---
## Troubleshooting Debug Errors
### Error: "SyntaxError: Unexpected token"
**Oorzaak:** Ontbrekende haakje, quote, of JSX closing tag
**Fix:** Kijk naar de regel in de editor, controleer haakjes
### Error: "Cannot find module 'X'"
**Oorzaak:** Package niet geïnstalleerd OF typo in import
**Fix:**
- Check spelling in import statement
- Of: `npm install X`
### Error: "Cannot find name 'Component'"
**Oorzaak:** Component niet geïmporteerd
**Fix:** Voeg import toe boven het bestand
### Error in Browser Console (F12)
**Oplossing:** Zeg dit tegen Cursor Chat:
```
Ik zie deze error in de browser console:
[paste de error]
Wat betekent het en hoe fix ik het?
```
---
## Veel Succes!
Je gaat leren:
- ✅ Debuggen met Cursor
- ✅ Terminal errors lezen
- ✅ Inline styles naar Tailwind converteren
- ✅ Imports en dependencies beheren
- ✅ Website testen in browser
Dit zijn essentiële vaardigheden als je echt met code werkt!
**Deadline:** Vóór Les 4 🚀

View File

@@ -0,0 +1,175 @@
# Debug Challenge - Antwoordenblad (ALLEEN VOOR TIM)
**⚠️ NIET DELEN MET STUDENTEN**
---
## Overzicht Fouten
Het project bevat **4 typen fouten**, verspreid over **8 bestanden**.
---
## FOUT 1: Missing Dependency — `lucide-react`
**Bestanden:** `components/Hero.tsx`, `components/FeatureCards.tsx`
**Symptoom:** `npm install` werkt, maar `npm run dev` geeft:
```
Module not found: Can't resolve 'lucide-react'
```
**Oorzaak:** `lucide-react` wordt geïmporteerd in Hero.tsx en FeatureCards.tsx, maar staat NIET in package.json.
**Fix:**
```bash
npm install lucide-react
```
**Leerdoel:** Studenten leren dat je imports moet matchen met dependencies in package.json.
---
## FOUT 2: Typo in Import Path
**Bestand:** `app/page.tsx` (regel 1)
**Symptoom:**
```
Module not found: Can't resolve '@/componenst/Hero'
```
**Oorzaak:** Typo: `@/componenst/Hero` i.p.v. `@/components/Hero`
**Fix:**
```tsx
// FOUT:
import Hero from "@/componenst/Hero";
// CORRECT:
import Hero from "@/components/Hero";
```
**Leerdoel:** Lezen van error messages, herkennen van typos in import paden.
---
## FOUT 3: Missing Import — `TestimonialSection`
**Bestand:** `app/page.tsx` (regel 9)
**Symptoom:**
```
ReferenceError: TestimonialSection is not defined
```
**Oorzaak:** `<TestimonialSection />` wordt gebruikt in page.tsx maar is niet geïmporteerd.
**Fix:** Voeg import toe bovenaan page.tsx:
```tsx
import TestimonialSection from "@/components/TestimonialSection";
```
**Leerdoel:** Begrijpen dat elk component geïmporteerd moet worden.
---
## FOUT 4: Missing Imports in layout.tsx — `Navbar` en `Footer`
**Bestand:** `app/layout.tsx` (regels 17-18)
**Symptoom:**
```
ReferenceError: Navbar is not defined
ReferenceError: Footer is not defined
```
**Oorzaak:** `<Navbar />` en `<Footer />` worden gebruikt maar niet geïmporteerd.
**Fix:** Voeg imports toe bovenaan layout.tsx:
```tsx
import Navbar from "@/components/Navbar";
import Footer from "@/components/Footer";
```
**Leerdoel:** Zelfs in layout.tsx moeten componenten geïmporteerd worden.
---
## FOUT 5: Inline Styles → Tailwind (6 bestanden!)
**Bestanden met inline styles:**
1. `components/Hero.tsx` — hele component
2. `components/FeatureCards.tsx` — hele component
3. `components/TestimonialSection.tsx` — hele component
4. `components/Navbar.tsx` — hele component
5. `components/Footer.tsx` — hele component
6. `app/about/page.tsx` — hele pagina
7. `app/contact/page.tsx` — hele pagina (formulier)
**Symptoom:** Geen error, maar code gebruikt `style={{...}}` i.p.v. Tailwind classes.
**Voorbeeld fix (Hero.tsx):**
```tsx
// FOUT (inline styles):
<section style={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
padding: '80px 20px',
textAlign: 'center',
color: 'white',
minHeight: '500px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}>
// CORRECT (Tailwind):
<section className="bg-gradient-to-br from-indigo-500 to-purple-700 py-20 px-5 text-center text-white min-h-[500px] flex items-center justify-center">
```
**Aanpak voor studenten:**
1. Selecteer de `style={{...}}` code
2. Druk `Ctrl+K` (Inline Edit)
3. Typ: "Converteer alle inline styles naar Tailwind CSS classes"
4. Accept
**Leerdoel:** Leren werken met Tailwind, Inline Edit gebruiken voor refactoring.
---
## Samenvatting: Alle Fouten
| # | Type | Bestand | Ernst |
|---|------|---------|-------|
| 1 | Missing dependency | package.json (lucide-react) | 🔴 Blokkerend |
| 2 | Typo in import | app/page.tsx (`componenst`) | 🔴 Blokkerend |
| 3 | Missing import | app/page.tsx (TestimonialSection) | 🔴 Blokkerend |
| 4 | Missing imports | app/layout.tsx (Navbar, Footer) | 🔴 Blokkerend |
| 5 | Inline styles | 7 bestanden | 🟡 Niet blokkerend, maar opdracht |
**Verwachte volgorde van oplossen:**
1. `npm install` → werkt (geen errors hier)
2. `npm run dev` → Module not found: lucide-react → `npm install lucide-react`
3. `npm run dev` → Can't resolve '@/componenst/Hero' → fix typo
4. `npm run dev` → TestimonialSection not defined → add import
5. `npm run dev` → Navbar/Footer not defined → add imports
6. `npm run dev` → ✅ WERKT! Site is zichtbaar
7. Inline styles → Tailwind conversie (Inline Edit)
---
## Verwachte Requests Gebruik
| Actie | Requests |
|-------|----------|
| "Welke deps missen?" (Chat) | 1 |
| Fix typos/imports (handmatig of Chat) | 0-1 |
| Inline styles conversie (Inline Edit × 3-5) | 3-5 |
| **Totaal** | **4-7 requests** |
Studenten op Hobby plan moeten dit kunnen doen. Tab Completion helpt bij imports.
---
## Tips voor Tim
- **Moeilijkheidsgraad:** Gemiddeld. Fouten 1-4 zijn "lees de error message" niveau. Fout 5 (Tailwind) is de echte oefening.
- **Als studenten vastlopen:** Wijs ze op de terminal error messages. Zeg: "Lees de eerste regel van de error."
- **Grid responsiveness:** De FeatureCards en TestimonialSection grids zijn `repeat(3, 1fr)` — op mobiel breken ze. Bonuspunt als studenten dit ook fixen naar responsive grid.
- **Contact form:** Werkt functioneel (alert), maar alle styling is inline. Goed voorbeeld van een formulier omzetten naar Tailwind.

View 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.

View File

@@ -0,0 +1,212 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R /F5 6 0 R /F6 10 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
>>
endobj
5 0 obj
<<
/BaseFont /ZapfDingbats /Name /F4 /Subtype /Type1 /Type /Font
>>
endobj
6 0 obj
<<
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F5 /Subtype /Type1 /Type /Font
>>
endobj
7 0 obj
<<
/Contents 18 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 17 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
8 0 obj
<<
/Contents 19 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 17 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
9 0 obj
<<
/Contents 20 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 17 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
10 0 obj
<<
/BaseFont /Symbol /Name /F6 /Subtype /Type1 /Type /Font
>>
endobj
11 0 obj
<<
/Contents 21 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 17 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
12 0 obj
<<
/Contents 22 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 17 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
13 0 obj
<<
/Contents 23 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 17 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
14 0 obj
<<
/Contents 24 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 17 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
15 0 obj
<<
/PageMode /UseNone /Pages 17 0 R /Type /Catalog
>>
endobj
16 0 obj
<<
/Author (\(anonymous\)) /CreationDate (D:20260224202513+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260224202513+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
>>
endobj
17 0 obj
<<
/Count 7 /Kids [ 7 0 R 8 0 R 9 0 R 11 0 R 12 0 R 13 0 R 14 0 R ] /Type /Pages
>>
endobj
18 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1125
>>
stream
GauHJbAu>s&A7<Z-DsjA&V@iWKPu^L<1G@cCq+i_Ke("q>nX,uP2k9MO3[e?.I1PVX-/]HU]b(Umch^k#b4'qT&U^(]*.!'7mI*s/YHP[OqA+dhU`gO7[f]9,6UD4Z<.E-@D1uTrJ4bZf,dkq&Wm8E7&2X@Zbe1qV4.$i8aK7^$m[W\+@*jd!_l&_KP&@tD.u7u6%@s1O=,#0r`N"SoIc\0>qp><e,PM1g!.^,OPE4o$3lu.`8Z(r;A0ER94\Sf0bNmNrs'4f0Tgl4,Vq-Z1#pi.9W7Ki>V97^%sa;bMnZ[r,<][W!N4,@/ut#XB'_fEg1hBJDR1K@0p4kTQF?0.2HG2"`.Ha2.iLo?$4>.7^;c@((snnt_<i`sO4Ne;(cJ1Xd;^_eML9@Q?^'=8%4^6R(>[&q=*PL$1^^fQ'CbLj-I=t-h=MuR?p/qQd=1*HQ;Z/p4YCC=WI':+pUFW*29`,lhMi<pct@8[F[%=uBtZT>PO6t`\8%<Gl1?jGX3,@gX5H3E%IU)1OZMTO&$1-ZGn$0oc^2Q@dJ:gY6[3QMS\#]?N1Fk<#P:q,Z7*!`2jUh&.;kF>J&[F9#jX=+/<^Ynko(_m/(2Nd]K0@W_r?r#:GegA%'H<rWrA@Gm1o+(\GGgfj7"mVbH'U@n7%%-*n6X-=;`,iV0LV,i=NgK/;nN.&DVHMS+iq*>o;!C[U`Mu_ccf;R$MHpUZU(VEfbQ:%E<6B);J(@@K5K]*K@bm&spo/*CY03Tm4GR"h]oO2V9,6oN+<iIFTb/oZ2rmRW/n-+?r8-%[p(Qibe^EkT+]K$)u`EIRh]3+tN[qmBpTc8Gcr.F]$R62Nlod7GK`&(-YX<Qt_Pr[uI)bIjbe:]+3ND5mNUQ-Mn^_VaP)+[B]?^4Gjkj1\<JY]!Mm65Am$)Bk-d^BPV/<B+Z4uLKP^1N-%&X%`V-?7)EA*!BX'%BZ=U^RJ6EUW9aIP2=A,U(L@Y6[5(5uJaO64gsmchc-pMI/*8W;(LcfQ?>+gGmOsL*=qKa"TECNUbl*'mA`:[G!FFSEJr[pa'<JgH..RSuR[E6eJ6QU/M"^aT*-Dt.EU88`2g!il`T?"?n=cA^H@/4AMPfnnSP\hYHian+hZ<XDS/V~>endstream
endobj
19 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 951
>>
stream
Gaua;_,B#A&;KY!MWJH;W@H#qCi_,G)/(0YEL@kgEK\9qEU4f0D9:7NjZ"Ru1A'"/1)%Wi<rW,;="AgM=#@\P3P\2p+8Z+mK_QUA@LD*&LuR9$:UE3+^g&H;po_dA,s;V$h/'2lORQQ?Je"D34SDkoC@2<l;m((f@+p'W0PR_`35r__RY<L2n:;Vh_Ym>mH+roM#Y]P,Uf^A&>hYR):>Y3!4">riZd"tb1UW5TSQu]pS;r]#,IBVNq1>hSju$C5E>/i^N@@R*Wsf@Q9'O5=W?J<MJhY&5M0W-t#DWjT5^[,"J('LhBueiD[.IHC`+rbMAP[$;'G'Rl;h:7K8&hC/4964V&op?T$^MkifB580M$`L]"!fIR`Z5Fj6sC5d4>#9#K&PM)T<:I"_o]mJf<<T2BQ+kUs6=r)+St7cU;754Ds/"#;7bjQIk"FRqS'$6l`>2bD,`Tp\0?"UZDKI[8s@\2'1#d"Z5TFXYQi%?JuVR>c(L^-#)YZ&<P=ln*F3!eW&"=b\u"cE+1L?(%^IOY[EN::)fIR0?90MR2*2#9S_d>@djMKoL8O)cZ`9`\,J+=7-<Q5!NaJ@:5X\-Ya`s,I#ertfW?K6C=_=uYk"0#5(]r9=b.=/1TdgGYiT"/c2O3)44j:12B[?SN>[RLQ4E?GE%aBqf9u=74U,4a<X/BkLC,\fun!eo[C,r:MbRs-<70u=O5`TuH#(BJ7N2KfB4Cmr,MW7i0W<8BN3;EqC/esFjpGPS\G3dX&mA3m+lbaj,XrY&Cs5*"&h!T<9H!H#k>(q&c8aYm%29B'Ll'eXWjl-7mD1N;*<VB6S;5R;RoX$=6.*"=`N3PMdZBZ4a8+WlN0h":),uHJj\JKG/?Ad4;"frOW*jnVp_(M[<h.&F`d5%Q2-$bLSh2%bGQC,e`E*Y/MX1NF4.oW5PQY!n&]M?nV83h&aL#*nW_R)u~>endstream
endobj
20 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1197
>>
stream
Gaua=?#T!f&;KZL'si,VS38m;P;o7[/58@)MY<9N='BTR_>o-pViCec^O@QhX"C0M<n_DQ+;r5nqp%1IP^uIL<e%#gJ3O\AH-Uq[84LCBa+r^oihZ+k@%/N4jEQ8tqMcIZME"=Gd"UCd!"S1c+#%Y;]]IT\B&$5UZp9Es`ZT(X?Kes4(kg9q>.".QA+dR+G-(EQ::/]@inKH,4i'%_P;tIH8c':u.9tb8>kL.E0q@/!RD`C@^ZqE0h%N>VOF4+[q"9sLJ@pTkYLL<ImYTcp6c6#rf.u#3A$q7V>AgRSf%`C*-aKTc();dJ8`(==WBt+%q&STq)AjZ_R715OR9<?(=r;#p3\N>)WQK?(`l0[[6,^7M&'q*CNRkMS^#H.G$Mc>LW*7>p2qWfZ-JU^2TAW#-l4,OO/MXXNK%4%e-_s:*=%G:KAr^AS4I_YFIrbC#r?cV5R*M_U[#L3/0Ln4P6W>/j,bRbYJSBUP3[9\>cUnSVJ-cs3.*K^t9/A];H&lQrP_@cU:g.!^Xfg96aJ-c*p_0O8p^5Kdg@Q[_T'J\.,H4b&BBT&\\f*+-XibI:d-:4P-8NbGRJOjqpT%6;>lOTL':i7hE=[GHs1h">BsiU57c6\Ka1Ms';>l2Q(AZ9aoRh&4*--Xm#C_R2(:YrbldCd9G@9,NLEGH9LrK-T*F'.u)8s)pr1,U!(M2MX_iSEJ@88Ip?l%E(3X\l$6Re>Oqphqus%^0lB0Q6Bh*1p$RKG+iM92V>=OAp'KYO^"=I.8+p--4fX51<[%]f*b82\',G::/`0a86bS^:Z'IpH:J:>mgCG7qE&-V8^;h=QF4RK=;.,=p-XDfj@7j8ZhB4CqFp;VCD,+g"2.8f/0s#E*R.9/KhA-TV&4q7%ppcPODP\VrTAG5VB9K\3&>;.;X+;,\SiilFI:&S1^9;CIJFjG<DOLd=E[2)/\hN?Tj<^O;I]7Kkj8R#D!kEk&S@_u^ZrY4N<:h9*/`Q*[mu9T:f-[JsLt2dYpCr@Zfh2D_RX5]aBjkpS+sXn2,Do>-6E?W$i:T8Zaq$2R[%:CT2#\jNJdSjR4g`HTNRNs55TP8+ia>opN3@(B9qEHbZ,Hq*`UJGXfZ#/p,KV#Yl8Q3t?7)52um"K71sf2u)+3(&!(/#;;f.f+pshn5aS:,K(9F`6]Oi)5UdXATY]_-8qFPQ^HC6tlK#OQ!qg~>endstream
endobj
21 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1262
>>
stream
Gau0B;0/3d&:XAWfR'XI>SKa8!IhZUOs\X_g5K`Y3o_"oX-bE1"QZr#?[iFfN,]j#*mU@^$sKcV^]!ki(f:.9qDZOs/j_P4@_asVhgl(X+M<EOE:c,!NMZ9(,<6"TEJQ9XMp"Ta]m3l$4GQ!@K<;uML]i:)Se&d6-2624/E0NelN@Am8eUWD_&5EN)SWs.&IF+ffCsPLU4!t*X^gF=&Md4sX`pW7X3DR6V(*Y4cin>naNDKtQ.U@'kP5F;J+)'MiQA5RaAWUAQ0B9[FC@l5O8=G-Qcd0;>9(i:_PgoM:TCe$0b9ScKoV?:<f&FOX).E;*QL5@XkkCP?R9]!6[@RJ6c^QtM9jfcnTNV$OpF@+C7^4cB$NQu18p9LN43d#d.RP#Ga/%6jK&Ee6^_"`eu9=t_7>=HqNJ!`NJNU-i?+[X9OJWb]=kcP!*>f,KU6m:+5c-nP&$H<k>[ep^a]T'a)g[k/`aNb&?7#SRH2QeaUVQ/TYNNQf'u*UM*Q1SLBumX#h!^/'>kh+9r0-O%^[^XX2:3[Sj9U'Y2!QS)/YKB\Pbj41taDL?<"('YW]XO;01U=5PB`k'R7mc9p",&gW]N4hW3)THB90VkWZ.p*l&aE"EA/lQog/I&f1<MBcaBLCGM:&Ae:f=/2?hnQpf$khgmng=J&#/[gJ6HjsQA/Q=K]iM^Oe1Yf,C4d2BE<_:g5M@\6lNa745TWe^*6s$q;N4i8nN+UUsVRF8*_M</+/`<(f8C@$R,2*$i@l:MYO4NWg[6#=6I+`3PY.\221m'(BA0>YM6NX%sQ+7G!g],l/bJ3r8D!7t+rT`8#HpXQb'>lI>UjA%^i.NBcGqga.BMdi7TJ4=K1Q\e.3:2Q,bIY;d0B$8Yaa@7S#WhUL,aC%)J'rer::QO.9o67mjmC*$ihcJY(m`k=JV'$gBX8s=6]q%B^>^VmM(EG06A$*bH2piF&T,cr07hMU.4+,C2.QW4&@j_f)^K[/s]jmtGn\n-J,k.W!.Yeas`R`h07m5kdf1ao)DO8L/@!r;KIDe!6p-@qH3Pm/f`S;EmWQ?2Zr@>-6Z+UnNb#Qpu%e#;2=ZMAdOi_``bO]M^D%KqN3YF`bnCj9)AP4*kF5OH1lJ`90S#*<6a(ri5qiro0:#;A9J*COMc?cp9kNouR]8Ht-%%2YZ-%nco<%OBq$T;Z1aW'>0cITU->MWcK3YV:p\rjgNV:&?+Yt!ib`oClaZI/EY:LA%ug;LR]<lUr)Q_,#LknJq[Qn\@Q+q_5s~>endstream
endobj
22 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 504
>>
stream
Gat%^hbV*C&BE]&=55$`=OsYB=s"=c-7BcK?c7^$V9Ea+Yi1ZChE%\<JhV5eZ<Y0=rNpd(+`r;$mj&/%-IB<CJ-dJW^_BX:#&oh1b5cmc9B%oT&MPYr?m9W6p$m7-9Mg,;\,aN/P!ib=-N[<eB'fT?R*ulSP!"[,h9aViT75PDh<>VLpOCDMnWOs7hFE9B?_#'D:=bs\^2GY"gGf5$@7YEP9l.PJKeBC2o2qulI+4?0[0<SQO#'=5+_gD07T:MS@eSu(X/G2Ch_0-m2k:sa2m_XqV;G2Lh?;jJ%V$d=V*(hrV2)BfY']H?!QR81PVW8)G[*nk@aQ(*2fohXkQ]>Hm-k*$\aB?#Ao%"d']H*RMus#&VQ-/IX]eGg!dnkBg<K%:K82Lreh';,0-$_=mB$@VBcJl7LkRI4rIY#(3'e-(`3E-@Zg':/&u#ctl1*KCj]lAb/rYN^X7%Z(3eJ"f1#*51DP1p-/gk^UZ_-<l123G4D-Ra+0>MuDPQ~>endstream
endobj
23 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 950
>>
stream
Gau`R9lo#B&A@ZcG&kZcFQ,p`2n+H:aHV'/\2eO`=;V3G,.=!us1W8Y'#MGodjaO;6*ONamn(-07=ejAeb:m(\-V$Y2[Yd80EZ&-('I"g"g+[<XZ[]N0HJ0;pC,7:(f"l*%u7\R(lZ"k9u6=eDrDU;glg2EgMoh)"kP\,QOjZ_%^T)=Zu"r,mE9IhC1#$Nl@%'ohAFjcBK\FJTfib.n%e?Vs!iQemI`>[a)H/Wlok"/HT*;`4@4!jY46!<H5jE*bUV$/*2hZJpsUPs7Y!M\rj78Z*>U(iPH&1KR]P5MEbW](bI_D(L'P[R."q"Rm9nNUm9"$-UBh8fY(kZN:n"729sHcQiFjQeFWf\-,).dCb-U&0VQq1.)53[Mk*]Nt6!GiF6BSnd)'+,bQX.gPTBp.Wr>Xb.kUqd@^?+4+EEg[1%Dr??_2B*>IA$nb"G:?d`[B#1*4A.ENH5o6D2`s<j\FnUKS4l2C#3"Q98$Qc(W`Klk;&Y[NIN9;T?]7_paft2LtqPT4>#R]$Vu]5#'!`)s!@InHP\pDclMbihhP:.THW&`QQ!e3CjYB5[eVh1TSkj,&e:*n/J^J/1;X,iL#s"8N/^Uu/RtC1<An0Bam;ehR]'=r]PPA?&SB7Z'k3M[T9uAX1]q@(HYC4h4Ii-P8M/5RU"2>iH&[$nZNVCn#o<bZ_Jq,A7dTo*7bfS9X]"AC7VaSr5'i0UmHg7Q<`1?Q8;)GGBbKPgc^T2_o[(7RKbPr%$!=%ZkcfpZ+]47ZjSCamq;:d\k!qW^M0!pXW7%'=QmC-7R:Fro9L;8p5jB@PGPgi/3p(#4C/.T?+$;jd==uPTe/"kValc_]aIDrD(A/\IH`TReZ$e<BJd/f?%4gd;(G17^'>#_(6bF\N"..R0UcZU%$C<J;9c/OdKX*;O8d./_%p(G]EEO'B8lisD0GZDHB*0?bIKOE)IM;~>endstream
endobj
24 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1110
>>
stream
Gaua=D/\/e&BE]*;r%5lm4ajPPk$0Bj5SX*RT*KG/-Dd%[&mH-*(i[B55e6`jLDVI@'9b8#t"X[m`.EG0aoIZ/Gu$>!)=2PIE@"Z87oX_OcYO\+rNKQ=JO@X&XmdfED.RZ,;^@pFR=-AUsc-s-Sj-*-m5$!W>L_a`tDnWq7m.iV00Jk^r/VX8gEg@B!9QqrLkc]E?klrRR@T$Y.&u0T4X(*Z@_&c`V;U/]07>8HMeD=#=r1O#$n/WdR6\GpDl]B:]$4M+XogdNl;&p!2MIp%A091nD)l=k"o)BXmA0V7tDpr3!b:YMTZK)`l[Gh3S\6X0XY;j0.Zt^IGk3NEN-6mQ+8^^p@V:SJdp$5<u/?#:T8Q+S!Smr/ho<0U2CaUaMU*BoQTk<#?nT:H;EJMVNYa2!>[S#>PD&4c,e-bQQ^fa'^OdHQEu:9/7DK2MqQ=Kc`'?/;sPc;+S@qs;Ka.%[4[tl'hj!Y/ui6m`=.ri\H]ObF',:L^1Im.,hCRUI&H^u0/:pgMmS)Da&(<[q\Q"R/:I4G8)URFD3!PXFe14pJ,E%!,MM&0at@JVKrpABHung=Y?'I*1mWPk:ImsRf^Abm?1djMcdV3DgN3./Fa]aLrLSC]VO7QEKQ<d$57nA=Acrpc7(a0q5'JE0"(lbY?hhHoJk"0@%>g(1VY6sfM<(6Lh*:'9p&E"-da*7#5<_VBRV`H'_nm6c+sr!EVS^OpSkQ0.mM05-b698^1U5DXbhTT#<5'DdDY(En7OgdbR&QY$Ds:e.foEF*o0UC/S;J_gC/1jQmAFj&*_[!a["t<LQ-Xq$p[JPVUHQ)eBqJ?t_P8"m<a09$4j'Yr4")9sR>c=S)Ih2$+c-b+;InugC.5XhOV_B?=up\Ljt'b2Zef5+U9t];6,7a"LV\h)Y\RDoEqDo1_/_rb+lXV=p)fIC>VL"_Wt#^@o]LhG<:.$ud3HVWcQc@)Ns4dm-Gru7RIsh(>t#ZWTHHJ%81R,bIH=t+]/SX(>.PZ#(^=2)!dri6<A>%REM76j*BnCs0qh:/D=,BP"E=S!#9N>YSm+._E&YF%rUfM(gu1N9Im<oL_,W5!j^_4&q0):VMGJ[.Ae,OZrrQpK4+d~>endstream
endobj
xref
0 25
0000000000 65535 f
0000000061 00000 n
0000000143 00000 n
0000000250 00000 n
0000000362 00000 n
0000000477 00000 n
0000000560 00000 n
0000000665 00000 n
0000000870 00000 n
0000001075 00000 n
0000001280 00000 n
0000001358 00000 n
0000001564 00000 n
0000001770 00000 n
0000001976 00000 n
0000002182 00000 n
0000002252 00000 n
0000002533 00000 n
0000002633 00000 n
0000003850 00000 n
0000004892 00000 n
0000006181 00000 n
0000007535 00000 n
0000008130 00000 n
0000009171 00000 n
trailer
<<
/ID
[<ae7522970d3e617d2353a6d07b4c4975><ae7522970d3e617d2353a6d07b4c4975>]
% ReportLab generated PDF document -- digest (opensource)
/Info 16 0 R
/Root 15 0 R
/Size 25
>>
startxref
10373
%%EOF

View File

@@ -0,0 +1,638 @@
# 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

View File

@@ -0,0 +1,269 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R /F5 6 0 R /F6 11 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
>>
endobj
5 0 obj
<<
/BaseFont /ZapfDingbats /Name /F4 /Subtype /Type1 /Type /Font
>>
endobj
6 0 obj
<<
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F5 /Subtype /Type1 /Type /Font
>>
endobj
7 0 obj
<<
/Contents 21 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
8 0 obj
<<
/Contents 22 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
9 0 obj
<<
/Contents 23 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
10 0 obj
<<
/Contents 24 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
11 0 obj
<<
/BaseFont /Symbol /Name /F6 /Subtype /Type1 /Type /Font
>>
endobj
12 0 obj
<<
/Contents 25 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
13 0 obj
<<
/Contents 26 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
14 0 obj
<<
/Contents 27 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
15 0 obj
<<
/Contents 28 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
16 0 obj
<<
/Contents 29 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
17 0 obj
<<
/Contents 30 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 20 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
18 0 obj
<<
/PageMode /UseNone /Pages 20 0 R /Type /Catalog
>>
endobj
19 0 obj
<<
/Author (\(anonymous\)) /CreationDate (D:20260225061542+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260225061542+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
>>
endobj
20 0 obj
<<
/Count 10 /Kids [ 7 0 R 8 0 R 9 0 R 10 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 17 0 R ] /Type /Pages
>>
endobj
21 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 897
>>
stream
Gau1,9iKe#&;KZQ'mgA57-NA<[fW81_h8`ZT"):j'#Go;YS1F>oj,\Z,p=.M3\\k#YuVt85.`="T_Pb<Ada-Q"HLbF0Fl2-Qm<<-9,Jr%6c>FUg(9r<LR8HYPS#kaX[F)P@g.PW==ONnE[u-pEZ3o"XPJSE8/A]=#B'GT\.EPn3>%E'MZ]+soIaDQiY]trn+@Ain'_$ej\lDn]pn]CPm<E+6ZNG`VF6e`b!fKoD\$/KI:bKr3GoV6W;d)dmsWR6reD#4=o0=n2j47-'!*8+g=?L@?rF(-/r.JM5/nE<-F7?$(N'DSl`r!D:\pQ%pWMZ](a6;R]?;gd+H1@*mDLKX*hAtT:l1so%:WjAE>sM30cZ0Re(DJ/f8.=CP_\prf%tIA!m*ta\:E#DaW]YP38+@@2&;qb)R@0mZA/@tJPNk/L4+lH1EOEj4"K+!M=8q-8eDTJZXU20]4Vtlk7tP2?s+9>/mAKg;jZLJbdhc\)R?RA!j<V$Qm+AaFrr:Z'QQHo4K\RUhguJF,/Pma1-*9A=cG<^i5dGld)>:(TNO)*;n,E5N.;tekkaTAi&$"cd9npNG'G<H9SAR<T3'h#f)?6.@,4La:;PEdgdWiN4Z\oKhn9rtp95U.'i4Y<8B3E.Q0=9W'`6R!Q1c_8U]!9U8+JE>L=dh*>uVblm8WN4I'3%4\X.u;<\1MDj]cl;?^cf.8AbL:8n/Sj?T\sb#!\)r036V4("i@>%3j@h_`sg[<eUkZ;6$qIBQhO,<d^F-&/QJFS,,?IX*s*F'Q*60PZ7I!JZ2VN&6ZKtE796HS8t1<fKF,oBhmo]1Mopdp0<s/je+#FYUs.L2s65IPc6W1XlH.1qGN&P4B,a6p$3Q%'N8(EG==6i4%JOWQD_OT#_hIQ\)dHm~>endstream
endobj
22 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1081
>>
stream
GauI49lo#Z&;KZOMTu\.0[Y8],l&4r,4PllfiW7]JW#%JN"H[?8Toep^HEg7li\,/]2]5=P%m0arOb;lMF0XYm]WrS^h!`=7i]$hC]Rcq"kaa@D"(rMG:Rg[U_X"U.9W`?8D=>u*gmNP_)MsYRqG<eK.$rX+M!G]C%mP$(2cb6OG4sA.!dIDTh9"M[nd),c\'A1%CKf)816.1=X8\(Mf<r*?st<o3A;$L$&&od]bGY9&U4ejUS-"_RP^<U9Q6[K>Joq@KPaf!E0FmJH4+D'D&_+GXC*5?"Aah"X:[&.o*VAW&`s@kF$$9;gDG<^#2(53kO'oleY'j5=R3HG;OYD2p1c>tCCu7g,pF(EE9L%8_YQ]]-t+k<K%S2M32`G;^S_mDQD`=c50?[)lutMlm^!F2JR?4Sq!Q=loNQWgQ,;bQHU_J'Y5b6^A14-c]2^XI42Ir\A5H\5-7]T;o03G1TC%j)LXYINH;Qt(0YG7SJ_"+"A_GYCT\DSI>q$]@,#ebN5cbkr1q,&h9.Wa?97uq\I?%kbA&U:(qGu117,Q<4cW[uDpA9tuND]L^I\VN-O\m*Ji,26;R&V:;%9@POAj7pk-/M?[>NH%*F)C(l'KELk=gbKZF6fbr%Bk@DXCRAd?*VlR1B(YM7$%(mG"(4TPr%`#D-P/k?1rr`JL0<m<th$?g);LSg7Ok'c67rUgQl$j[GG_6b/r=N5!a@6P-6fsrrslu^lQL\\rqsAZ@LUa,#"-aj@4<gB^,&D>Qr<9X5Yp@k&kN(qkC-'"M_trG-l6t.\)-*(gIrYF9CP6pY#5;rk0"i"]Z$iM6iQ>W@eQ>/pOr6KVDBV7H1ErhuPj!;I2fS>Y:,Q-#kK3]*BKBGuG6#>>?I>1X0V%A,jd$!uFW4$Nlu61-ZrH"/5aKVnXXL?TXLeQE;6`HflF+0qdC9K,[kp&oVP,/#__7*n#/cHiM+:dh:871%tI^m8?T)f\k\s9G%@E/5n#*_W1^iqVZ(mCI,o@q@n\@O2on']RVN&Fp37uk%0Aa[JaW\heabTpZ\*9od.AdHangVFb/$NF^=]n?ZWNe*5mal@IQ4m_t%U~>endstream
endobj
23 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 984
>>
stream
GauI4a\q.Q&;KY!MWP\Q)Bm5.'QY/u$-Q.r+eGYnjPi->UF`jYSNu`;dp<Zq:iIV]9%i$(qWn.2'#<`DeUP(33:Yt300i(tV]Z[Pi*SdrDr%lj1K&FaUSS)<2![+ne$R]p*IdAmVKD3<WhZD1JOsR?.tDon;m]%1[GkqiX*&+i?p*Qgc9%+29FkgantBJl>`p;2ihE!5\B)D;R+B`\RI)Z$4nOeR5T[b7pWmK=6Q"\P\ek='hQJ!mYQ+C2!K>_>nmXK&8,N2AcbManQeVnb4XNQeHj&aRBC!<Nn]G'+/[btO\dYX^0!?ZQLA!&[&SeU$0SUr:M]kkNZr;m(==6ZE"+2ZP/;N7BL"gWM`o%J<R:$&*N&!IfC$&]Q0Z7de):2H/@50Am9^Q`'9Tthp3BU\-a-aQWh$Kl&;i5oD!_]romfo't3kKnQqs&;*i[U=>H%A/qJq=SK#'pCeDC?XEEON1EJuFlY%a-!9?to36OG8a7&]A6ajp2Wi@9D/>_Dk\.*!BUk=.]C,1A@1f%3c_rcWI4fLIG%t+hdNF;aRE#D-df4Fl$0l$V^3`*DF%V3[K(*a^fr5!?L2SM5N6UJg.kFVGJ%$/J?O.P^'n\s2[fHk7Ej?b9A7.Do"3t8qi=?Oqicj<<e@h&ek@C6YQm,+28TqK%$BCGnASP_.%t*[!SG93MdFd.mH(JdT&t>gT4S[k@nfp^cKUMNWH$#T.0@t/h($\RC_]>jXQ<a\34V!G^.toKbDThdO@9Ia[(iqL:6WNFh?G(j`s"5duq39OujVgk:nDDNDI5+q`a,L)17np_%i+M-FiGlP9e?q.:qD8J"=>IF3`^F5!nbF)S_\VW?eMq-`(tSJn"24?8!U;g6a5Q%Ad,paLCmR;hFR7q,#4QQdXo715Im[e$:Eu@[nq+<qk#WOZ>pR8B.^/%#ECE7nAuoE0V'g3?=")I]^,rp"p-::En"MC3>HgGZ.8^H%?!us40R0$i~>endstream
endobj
24 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1117
>>
stream
GauHIh/hR6&:`lHfR"j2)qC,5YIl^qP+-'HCZG,F(L\((0GZ%XiL7NqCE$q>#t&@Z.El13[?C:YTj8-/I<kQkC]LVZph@Tr$cj."-'OGJ_`/`c)t88fS?aga!b)`t/n3Nl?s+6M8I@"CA!'1"V_)U39gqUH==-=u*M:+&%Ku,&Fa%L5a".lA]iK0>KH^PWE.'`S0eaY:FMdQ>7ik82\e;ur5`-U4Uf`p;d2&'e-s-D0407`UaB.iU(rF3J*YN]eMqWGAXC7+5$XSBe^_9mTJ<AN$9oZQ9NETij\R!p^=**J']5slOF4stW\A<J#[JJCd/AH":r:L.1UEh%"6FK"*ZnA\`VkiEWmeISAOV*A71s%gWh@Q)c/51X<D2DM&d\6(&^muU>45;P`5@cG0qqXZtn1"D5.tguCs*4G&Bll:galDjcp]780&=d[iDL-:,U5Ta$VGVXPU<JY`hZcBUg=OK.mS%sVO-4X>%r^rkn<%N4<Jf&oHTS+QcrspN(?\YRXV0R!8!mMW[/WhB.185ECYpVM7^R71",%2Cj=*`R1cYjd#`e3mK+KD,]J=/]lCC/g=8km%9'WmQAsFg])cQbG"(>u8GGG[V?-j\<[d9Co2qqqFl(ZFj?8*3JYuh+)06H<teV1!cg2$V.QA.)Vk@#DahS6mp*H?CI?>iN><<\)^VK#^hgKImLSfEPGGEcU,P&;3F9+81(J<TKVK6H'>G$\/S7:f_6nUPYi[:WG_DEufZ(*,PE/?nX,r=Dm(q`j_M:e/G,Q)E%N<-h!XWPk3A1c\[iF8QNJEn%/hA8nYPHVoWkeec?Y(SrOB"GURV)C-BH9)E(SmQ=1*$5QQ53G!Oi"%LQ8bnuNc%hn7$m3h/=5UL_DTqq-Z9L^cF`'54D;'IEZg0PJ(FF;I(r@^XVM+Nfpm1ha$aUrVb7](?`8=L):>YHf$U(?I3`id.Y)1u;gC(36n"2i!#FI0Jj?t61`e/EbEZGLK;W+,SB=mlYk1R:o0jZVRLJ6>rYE,=-"Eh]#$OSc!q"Vfm5$lFo9<@&$/r2=DopLGH4ju58p$SttdWku>`_*'Y[!cY'k%aeMKiUMs^E+_db`R:-R%t7NTXqf#Z0OXAdj-'uI~>endstream
endobj
25 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1037
>>
stream
Gau`R;/b/B&:WeDh"EGLC@boT8P%`@8(C43bM$d\,[O_0_'Q?Ba.]9CIMY@c]7FXlBi?8_5!*uGh=l>K6@]mFTApRB;[+=>-r:-$'IY=KZ?$jnSKAl`hAQ&Y(c-p(4i2P/L;u?"'=/qc_H=Z"$7'13a(O&;U]%GGNnZDu8P6iF#^Tl)9FpZem=j;B?A9>GhO*rdH#`d+0g77fldJuCh<V.,"F^L,pYk',q!)[__)pikcf!mTCGr\Q;3sT9RX%%W3.U?2ZGZBe?a`7W"%#"B=t!V8S8I7M3e<?pC8RKoq3l]s!Y<*Lk[r"16E06J\FE8DY`REQHS=MZK5ItoV1c)g29*_D,.Ek(.H*"BGEj$ld3cT@9GRVd[h\c0KKh+pqeH,G""0sc5UhY2ddUE9IcjU(*DD,"i1t41:`:S\]X+?4U]kg\/'%'@PX8K>+CJ,K7SE.&G2.rEbdH1Ii-0m_@S)*7U)J/E#;?meE,YGn-Xu=)6I/]r#70i38nH8,Ph&39Iir^>`-srpmr;n^<]qm+/dle`LIP)#<,"?q[q@MrmUbLEd@u0]9IR#bh@dosV[(6]c@^3T4i1MtJn,VrItEZ?i+gV'c=#;NZGG)1UKYPOW\m2P-B.Pe3)Qdgn^jbJO+R:+$&:(_2,"]V=-giYEJ@q/"-0R.!_7RcjNjX7Il8H],iB!`<ASfJ'AlC<X[U!f]AXuYRp$<!;ktHcN6GAAQ&CsO"nGB&<3A#tV-;WPdXbHIJZ3%J+b$"k`1hKHmO)l,h^c]nqg7j7KZlA5QH`qhLXn$+"C\dZ<Njc,U3L-L3,1F<U>=j8)nStAms1qg1^`:,D]9j\%A*@Zi*O)^]o3-pL&o[[pR5</S$+u&TnMS1&q#_"L5+O<)/(0c!e'E*NP%MinFeBt`>*FaG!A*X`$>UC\JN'X?Uj=]$T?j-3)*m>$ad/aLf,?mJ.)"Y5/ceRk9Q_qcL]n5N`Cd'3(h[I*7*?Dl!0s`>W;%#kZ$1@^.LNYMp$$OE/HhaO(ZHHX2UdRPnjoo!<Sg!b6LM(~>endstream
endobj
26 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1231
>>
stream
GauHJ;/b2I&:W67\38A6:kkboMTT/`V+DFICM0?V!5HT5X!j7S<0EI=hsVu8ZL$4d"q6D$$RCeR]C51b-jCA-f).--JBJeT5;HUdiig-#&J.ar.e^T'XL".Kpl]][>S>%&md`m_8K''$8kC'.rOYaC8La)fPS>8MUAk,C[E%7F1QBjK8eUY"NtmV,551c#E-^^\$&$\+4nNZ7\IO`[+n@6NS6\X@F&>?8$SF'Y<c$HLN&cIanWf.II'fpm6\)7iC,N1NS\pEX&=+6+9)G,1FU;'=,W0*:o&M0ePIgSk7?Wo);C50sQqZ-$@5>B^'"H++^HlW-&r\V30@)OPD7S022$(Xn0(.&4TFJa<oB!ht]5LtC5mrmo??DQEU`s4)J/#'cC'WNJ,(<(^Eo;C07AoQVj2kTd%LYkX?:6+*FsEfXC&Hp'a:"BTInstNY@0?#20idj9^A=]Y+Pl!mPl63GTaW+h#g.Y-X$G3RH<er#WtG.`)hLN#`jf<p$Wr=<nf`>hsIF\NP5SM)"+YT8RAr2,mMPP-.?Fjl08>JF,b<\ZE2b2$`e&[g?Phn-X3kENK,G#;AC73T$i(9J+WY(pk)=[ds9L-C=qaUmHug?F3L%'WX@nb)7t7#WbPbRGWLOISlCuX#^k%]F(/$L>5N6$ogA`Y,SRo4EKcGE__dIP=?cluH9TC$<o"%?XhhU")YmWK-:@dp&8ZO'+YBjQ6rG&*+]HHB8U&*Mprj7G!Sq,8MIXS-J5]n"=X!nZ;>s%)$pp!:8Gn!-kao50rsOB?TT@]N?EUSPcuK#!RFTCVEF%^>GAn&P@X3,U41Tl6kBbOK]<KAPN>N);%A[D+S9KV^CboseC9Bk[*K^[EE"\iclkTqH2V)98,.F^9XZ.N%dbe$a4gs()O?+%^%SUh\b_3+[Qp`*(NUB?h/Ri4M(4484WV=5<j(Yk4mE;rQ#D`h'Ld24pHK27^SoXcSX*%GRFpo8dlQ27?kW2KIoONq>F`i/.9C$K>,9_.JDlc\->@Ni#>plT&Df?caoBdIV&,^qLOJV>g9%\Q9FR2FgZAt+#"i[>a36G[`EOBJHgPOd?rka+l[%>;:(sM#R]n"'D/E!I4i6#t:F8l#%GL#C6l)GIU`M3;?:B\T%:ch(3F?jhtm3o@&S`gAH;6="%-cedW!e/bm<=7akG<;6mUDus/:Is60Ur7Vm($,6V^YH\c9m*jgYdXG+qNReD8O*cC!8]r3_gaQ~>endstream
endobj
27 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 630
>>
stream
Gat$u9lldX&A@Zcp6]BfMBmj=E_P3*mA+Sse!tfZN_ILo8c_`bo=JC]0m+IK(c/Mqp?fan]4+%q@GQ95!*mN_Ia*C_Lf_@p&J3j:V`=Gl0OKZ.VEu>UN""*r%[#AZm8KiIfiCrZOd*e'_r*m$<>JVJ#E/Es@[%Ra!AT`e/[k>,mOR1/WVF(d7cM[QTmTp3[eDH&c8mN@h5-[Sgj=t4hHU@6K"->pV(5kWC!3A;^U?_?Y&$h$qX5lOcOc^C=BldhU_q0/kL0<o&sSjYO)<]`Ke'JT5=WOLphCnWFn(nmA>UjJ->Db7.XO9E_QsbG#W@sXb[#O[<6$D3nmM]lnVP-/g/`E_>@k?.6H&KQE9b3uG-8+#0eRUmgE[Z/Fh<H:nFmn6/JLW$$,3h0331l>_N,Xhs)c^^U%!Dr_]1JMkdJ8<CiR#oclq52]YZ?a'XQoZY)WL@H2tEp"CT^]\;MR3UHs?1]bfimgc3i5_@Z>]GMbR<HVJ5f8ra^D@7519gM?2jhcS>Z/1'#R/.i<He3J[G3MMmKI`I50I+'hbKX+/E"1'k<(\#iO>,Q?4PiTA,;[\MIq>/'20Z*otSAKl!"lI$YI"n%^D7t=;0&IrCOu+[&:npC5i#d<_=A]~>endstream
endobj
28 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 955
>>
stream
Gaua=92jS!&:j6F'g"Q4JIY#0j[ZgH80QFn-uh9/i]+deZ"5.A4SnBU2?lJpN8`]<4;0fWQ1XIHG-q`,5-4Si'7aR7%Xp*>*;ga`JdCj90^q/J*EHr:*jH^1na-qIGoW`)nAF4f%>8e(fF1<3pB7X-U5lCRfoB"R(q;_<\WhDJ,i%bFU?.#Be\3#IXS-dm=7;(<Q]5uJLXia0"M,9HkRuZ<3$%kB^GnHuf+&50UVZW]C,"KJrtbOGEfFG\kT*9_?uAQNF^4Ffn[ioN:8-C5<J6X@BZWrKa$_<g[HLbiX"0DnbutO<.Nk,)J\Y$WdG:ci@@AJ:g/$Y]NI!^?\X]cG#i`@a.#gem%SB+&b+sSW@-/"QE)c*nIX>`"<jOKUDe?[:DmOAGpIf+0X*i=9@XCl)/=1'WPg8i2OQ:Xa<,o@86*0\k^-`q[.<W?q$iDfEH+5Qnp0R!^R@JSV1!2^A4G%pu(%hO&`l[06pF5a\#5t0hiP]ZU"[OTBHIdk^@!VZm]VRk6i;n[9S1^/V83KHJUoQgsR!,Ur/u2Vq@glpY?n>Nh9feT$"hDl$cX`tlP[(Ar$=o,AB'gs:\arUE)Q/_7PJ,r%8D^9.A_i0aHX>Hor_k-kTi"s4E-+*fj5aPR4EkMoS6%HOZ!clam:S^^8>Ge1S+k@T,RW\YR%j7IlEBb&9qX_=5cs"Xf9gU5q'Zr.MBA)^KH7/e4?HVJ,s`)p?.S-^.$#mEEie(0o&;*aH&MANYP%83=TW33@CB)(*$1nY(@\4q8lK*fc&d,7\QP5[]S9'8/1+d:0/+IY/P9_5T[e?dT"['UOi6:cCb?HIDmrIfZh;=J"0$1CHdrQ$HCY_cH4Jfh6jT.54WlXO!S_"'G^Y&I6CEg]7=!^M0M'b<S5c%gs.p[Ql85K/`%u3BYtRls"+g!]oaoEsW,-\+1k$3H#*I=7G\`u!q$S^fd.d~>endstream
endobj
29 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1316
>>
stream
Gau`RgN)"=&:N^lHMt)0,aCY6NGcD=CqtdmX:qAqf%,/J/AqMih)pjjEk[!,G@<j<iFPXcdS+#nl'^f(O[6iScMnn,i6$ZN[1"eoCBZI<%'1>*XkluP/i>M=-lSqTR3jYeAtW;qlQJ9R#ZETDP7qPA<*+WIW\[WZ@T&C5L042%ge?8*Ceb2j,`43G"pb^>pZOG\:bCj!O\sCs,=]c_k:9s4VobdCjhpn)4So20gl80i([o/nK(V;`3(-b(.?S1u*XbEe=Y5-g8\)4d<,#5:EmT4$Umtr?n9i.jko/PLfEkosHiR7C$K,r:9g=VFiGN)GJTDDNp_"bbJ5Vn0\FA=ip@ajqjMnQ*\XfQO#KLn2OIST^_M-$Y*XlH%`A8;a%/hR_:`+g,Y'C"_NdmNPgr9'!&?3;%5rV$]30iCJ'i+&I!nS[V_3,&Kb5cDeA9`3YSOalKM>/=*,U1R1RXFI4`D#N,<acPp*f'3m#i&f.&)\@%?B3[Qf(O5.W2c,G='hP?PLp6EpY9qTg*:FDLB7G+*JHsYNFEg-gq^lrkCm>d0nA5;4$14[0j^tAEbUgZCa&/3\=8FtEg"p"%3/fsS##0[XtXuPF8C,BUDSU4oD0oa6LK4-4$.s7m^E!$1?mj>)5\UtU`=:=,YJ_68d95mh8)jrgGQ.^O"tPWn5&,u]417_J\P$2_%XQ+U>#+tiiJ??64G.'L4632;.^AF1B,%?H9;-'lL+4l"S9ak]f#_"brb2?`>4Pb]S[!lMQA=-!CO!N:L,]sMV:JkOm9+Pb&re[UEdO5qLDJ?6VLcqH3UG/0!Ih&PRa\]\^]jsGuRqE7n*;$O7L,KhJ@@TZK^,G`k$lYODXQ9K*BarPLe<no>E<hdo`Aokp10!cG4UH[Bf$>VQ&]AL5GC`gd3*;rpmsD#B4B5%6U]m0OD=\Qti;[NV?T?IFdAj[$btg.tTR139FL()t3BX]1QgsEn#Y>g4)l2`;Sm6<Mk:0L</E[EDg7?3`&G<CH#A4n(;6V7QEt+_M"ukqB\DA"/*$+_Y"h:)&@8*5oblWfK*i8?#,Yih-gP?&V%7+ctg+_XH%\FhmQ#`VG"F\=;FoSTU6L??GQLKMSIbC_f'Q?)gX1kMkj]0'Jm.[oJ;Dk<]-E_PdU`*/!7AT@Q$,H0j4Vu<<B62/HLk]!EOl>!(SpE5'dB-%Ad1ON']\+_YKWbAWE4)MFr"%+ABe/1k;fNI8pS$jB"JX0-<X?@RQAZ^"B\0O$OXKEUC`Jl$5BnF*K[:V\.c7-am,@]RpG?nTZqkEs`Sn=iLqX._T<^MiH=u)pHl^6gm78U\R.~>endstream
endobj
30 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 410
>>
stream
Gasaia\K`-'YNTZk1E,4[c8Vk8&A0(M?:1Fob(FeD3Y:Q<r>.AZ`"uVkUDhS]GY9m(oSJ;hK9=`BM@h3(l-t"#pQ&*\C9<4a',>D=laWGO\eFMd^M8]Y,=n*U1Qt"@7V)6$s.VO%(;r2:oO8JV0"o"o>+WI(@>pk#bZ5Ii_u,%Qb34i1nJ--38\i4&GcAiqPE4M&J9mqY0/ZH%-"QPQC5:Fe;7f24*X/NL^"4)^/At_V*Z(l]KY!>QO?0C2j#jH/^Oa'nUB`KoI!C\fJ<HcF6PWLCK\EZk8-'o?-9QDM/<&5Jm#8i7'mO-C&um$gfMUS+*%iu&B]U'.;!IXn?6hIhTmY1?'8cr8.AUPjnZenQmU?q28kCD#Z$Af7nh+U*7qg.bSot28bYY_c!5>tmi1FNN*-~>endstream
endobj
xref
0 31
0000000000 65535 f
0000000061 00000 n
0000000143 00000 n
0000000250 00000 n
0000000362 00000 n
0000000477 00000 n
0000000560 00000 n
0000000665 00000 n
0000000870 00000 n
0000001075 00000 n
0000001280 00000 n
0000001486 00000 n
0000001564 00000 n
0000001770 00000 n
0000001976 00000 n
0000002182 00000 n
0000002388 00000 n
0000002594 00000 n
0000002800 00000 n
0000002870 00000 n
0000003151 00000 n
0000003273 00000 n
0000004261 00000 n
0000005434 00000 n
0000006509 00000 n
0000007718 00000 n
0000008847 00000 n
0000010170 00000 n
0000010891 00000 n
0000011937 00000 n
0000013345 00000 n
trailer
<<
/ID
[<7a0979c2f1194bbadc3bbce6ebad8598><7a0979c2f1194bbadc3bbce6ebad8598>]
% ReportLab generated PDF document -- digest (opensource)
/Info 19 0 R
/Root 18 0 R
/Size 31
>>
startxref
13846
%%EOF

View File

@@ -0,0 +1,231 @@
# Les 3 - Keynote Notes
**Spreektijd:** ~55 min | **Hands-on:** ~75 min | **Pauze:** 15 min (10:15-10:30)
---
## BLOK 1: WELKOM & MEDEDELING (10 min)
---
### Slide 1: Titelslide
- Welkom Les 3, vandaag: Cursor
- "Dit wordt je nieuwe favoriete tool!"
---
### Slide 2: Planning
- 09:00-09:30: Setup + Installatie
- 09:30-09:58: Features + Skills + Request tips
- 09:58-10:13: Live Demo nieuw project
- 10:13-10:15: Start Hands-on
- 10:15-10:30: PAUZE
- 10:30-11:45: Hands-on vervolg
- 11:45-12:00: Afsluiting
---
### Slide 3: Terugblik Les 2
- **OpenCode problemen:** token limits, EACCES, permission denied
- "Niet jullie schuld, beperking platform"
- **Waarom Cursor:** voor developers, meer requests, ingebouwde terminal, Skills
---
### Slide 4: Wat is Cursor?
- VS Code + AI ingebouwd
- **Gratis Student plan:** 500 fast requests/maand
- **5 features:** Chat, Inline Edit, Composer, Tab Completion, @mentions
- Hobby EERST → Student upgrade later
---
## BLOK 2: INSTALLATIE & SETUP (20 min)
---
### Slide 5: Download Cursor
- cursor.com → juiste OS versie
- macOS: Intel of Apple Silicon (let op!)
- Windows: Installer
- "Allemaal gedownload?"
---
### Slide 6: Account Aanmaken
- Sign Up → **Hobby account EERST**
- Email + wachtwoord verifiëren
- Student upgrade: cursor.com/students (later)
- ✓ CHECKPOINT: Cursor geopend, account gemaakt
---
### Slide 7: Interface Tour
- Activity Bar (links), Sidebar (files), Editor (midden)
- **Chat Panel:** Ctrl+L (rechts)
- **Terminal:** Ctrl+` (onderin) → alles in 1 window!
---
### Slide 8: Terminal Setup Check
- **SAMEN DOEN** - iedereen tegelijk
- `node -v` (v20+)
- `git --version` (2.x+)
- `npm -v` (10.x+)
- `npx -v` (10.x+) → **nodig voor create-next-app!**
- Help bij errors, CHECKPOINT
---
## BLOK 3: CURSOR FEATURES (15 min)
---
### Slide 9: Chat Panel (Ctrl+L)
- Vragen over code, AI ziet hele codebase
- Wees specifiek → bespaart requests
- "Analyseer dit project" → AI leest alles
---
### Slide 10: Inline Edit (Ctrl+K)
- Select code → Ctrl+K → instructie → Accept/Reject
- Voorbeeld: inline styles → Tailwind
- Voor: kleine aanpassingen, styling fixes
---
### Slide 11: Composer (Ctrl+I)
- Multi-file, grote taken
- "Voeg pagina toe" → AI maakt alles
- Preview → Accept → instant
---
### Slide 12: Tab Completion
- AI voorspelt code, druk Tab
- **GRATIS - kost geen premium request!**
- Benadrukken: gebruik dit voor kleine dingen
---
### Slide 13: @ Mentions
- @file, @folder, @web, @docs
- **@docs belangrijk:** verwijst naar geïnstalleerde docs
- "Hoe werkt App Router? @docs Next.js"
---
## BLOK 4: SKILLS, RULES & REQUESTS (15 min)
---
### Slide 14: Cursor Skills Installeren
- **SAMEN DOEN!**
- Settings → Features → Docs → Add
- Toevoegen: nextjs.org/docs, react.dev, tailwindcss.com/docs
- Test: Chat + @docs Next.js
- ✓ CHECKPOINT: Docs toegevoegd
---
### Slide 15: .cursorrules via Chat
- NIET handmatig schrijven
- Chat (Ctrl+L): "Genereer .cursorrules voor Next.js + Tailwind + TypeScript"
- Kopieer → maak bestand → save
- **1 request = goed besteed**
---
### Slide 16: Request Management
- Hobby = beperkte requests
- **Tab Completion = GRATIS**
- Chat/Composer/Inline Edit = 1 request
- Tips: denk eerst, combineer taken, gebruik Tab Completion
- "3 kleine prompts = 3 requests. 1 grote prompt = 1 request!"
---
## BLOK 5: LIVE DEMO (15 min)
---
### Slide 17: Demo Nieuw Project (blijft op scherm)
1. mkdir + git init (1 min)
2. npx create-next-app@latest . (2 min)
- TypeScript, Tailwind, App Router: allemaal Yes
3. npm run dev → localhost:3000 (1 min)
4. Chat: genereer .cursorrules (3 min, 1 request)
5. Composer: HeroSection component (4 min, 1 request)
6. Refresh browser → toon resultaat (1 min)
7. git add + commit (1 min)
- **"Van niks naar werkende site in 15 min!"**
---
## BLOK 6: HANDS-ON (75 min + 15 min pauze)
---
### Slide 18: Opdracht (blijft op scherm)
- STAP 1: mkdir + git init + npx create-next-app + npm run dev
- STAP 2: Check Skills/Docs, genereer .cursorrules via Chat
- STAP 3: Bouw componenten (Hero, Features, Form, Footer)
- STAP 4: git commit
- **Let op requests! Max ~6-7 in de les**
- ☕ Pauze 10:15-10:30
**Checks:**
- 10:45: "Wie heeft npx gedaan? .cursorrules? Component?"
- 11:00: "Wie heeft 2 componenten? Requests over?"
- 11:30: "Wie heeft gecommit?"
---
## BLOK 7: AFSLUITING (15 min)
---
### Slide 19: Resultaten
- 3-4 studenten tonen werk
- "Welke componenten? Hoeveel requests?"
---
### Slide 20: Samenvatting
- ✅ Cursor setup + Skills/Docs
- ✅ .cursorrules via AI Chat
- ✅ npx create-next-app workflow
- ✅ Chat, Composer, Inline Edit, Tab Completion
- ✅ Request management (Tab = gratis!)
---
### Slide 21: Huiswerk - Debug Challenge
- Download zip van Teams
- **Fouten:** missende deps, syntax errors, inline styles
- Fix met Cursor terminal + AI
- Push naar GitHub
- Post op Teams: link + screenshot + beschrijving
---
### Slide 22: Volgende Les
- Les 4: Effectief Prompting & Iteratief Werken
- Huiswerk MOET af zijn!
---
## TIMING SUMMARY
| Blok | Onderdeel | Duur |
|------|-----------|------|
| 1 | Welkom & Mededeling | 10 min |
| 2 | Installatie & Setup | 20 min |
| 3 | Cursor Features | 15 min |
| 4 | Skills, Rules & Requests | 15 min |
| 5 | Live Demo | 15 min |
| **PAUZE** | | **15 min** |
| 6 | Hands-On | 75 min |
| 7 | Afsluiting | 15 min |
| **TOTAAL** | | **180 min** |

View File

@@ -0,0 +1,561 @@
# Les 3: Cursor - AI-Powered Code Editor
## Docenttekst (Spreektekst voor Tim)
> **Totale lesduur:** 3 uur (180 minuten)
> **Totale spreektijd:** ~90 minuten (verdeeld over blokken)
> **Hands-on praktijk:** ~75 minuten
> **Pauze:** 15 minuten (10:15-10:30)
---
## BLOK 1: WELKOM & MEDEDELING (10 min)
### Slide 1: Titelslide - Welkom Les 3, Cursor!
**[09:00 - 09:02]**
"Goedemorgen iedereen, welkom bij Les 3! Vandaag gaan we kennismaken met Cursor, een code editor die echt gemaakt is voor developers. Dit is geen random AI tool het is gebouwd VOOR programmeurs. In de vorige les hebben jullie gemerkt dat OpenCode wat problemen had: token limits, EACCES errors, en niet echt ontworpen voor professionele development. Cursor lost dat allemaal op. We gaan hem vandaag installeren, je eerste project bouwen, en je zult zien hoe snel je daarmee dingen kunt maken."
_[Glimlach, maak oogcontact. Stel de energie in voor een praktische, hands-on les.]_
---
### Slide 2: Planning vandaag
**[09:02 - 09:05]**
"Dit is de planning voor vandaag. We starten met een korte terugblik op waarom Cursor beter is dan OpenCode. Dan gaan we Cursor installeren en je account opzetten dat gaat snel. Daarna laat ik alle features zien: Chat, Inline Edit, Composer, Tab Completion, en @mentions. We doen een interfacetour zodat je precies weet waar alles zit.
Vervolgens duiken we in het inrichten van Cursor Skills dat is belangrijk, je gaat framework documentatie inladen zodat de AI beter kan helpen. We maken je eerste .cursorrules file, en ik leg uit hoe je request limit management moet denken.
Om 09:40 doen we een live demo: ik bouw in real-time een Next.js project van nul tot werkend. Dan krijgen jullie rond 10:00 een praktische opdracht. Jullie bouwen zelf een project, en we hebben een pauze van 10:15 tot 10:30. Na de pauze werken jullie verder, en we afsluiten met het delen van jullie resultaten, een samenvatting, en huiswerk."
_[Wijs naar timeline op slide. Ga niet te snel. Geef studenten een gevoel van progressie.]_
---
### Slide 3: Terugblik Les 2 - Waarom Cursor beter is
**[09:05 - 09:08]**
"Laten we even teruggaan naar Les 2 met OpenCode. Veel van jullie hebben vast gelopen tegen problemen, toch? Token limits die onverwacht opraken, EACCES permission errors als je externe packages wilde installeren, en eigenlijk voelde het meer als een AI chatbot dan als een echte code editor.
Cursor is fundamenteel anders. Het is GEBOUWD voor developers. Cursor ziet je hele codebase tegelijk Chat werkt niet op fragmenten, maar snapt je hele project. De AI completion (Tab completion) is ingebouwd en super snel. Bovendien: het Hobby plan is helemaal gratis, en studenten kunnen upgraden naar het Student plan, ook gratis, via cursor.com/students. Geen creditcard nodig.
En het allerbelangrijkste: Cursor heeft een INGEBOUWDE terminal. Je kunt node -v, npm install, en git commands rechtstreeks uitvoeren zonder naar een ander venster te hoppen. Alles gebeurt in één editor."
_[Maak oogcontact. Zeg duidelijk "gratis" dat is een belangrijk voordeel.]_
---
### Slide 4: Wat is Cursor? Features en pricing
**[09:08 - 09:10]**
"Oké, wat IS Cursor precies? Het is eigenlijk VS Code dezelfde editor die de helft van de wereld gebruikt maar dan met krachtige AI features ingebouwd. Je krijgt vijf hoofdfeatures:
**Eerste: Chat (Ctrl+L).** Je kunt vragen stellen over je code. De AI ziet je hele codebase en kan context begrijpen. Je kunt vragen 'Hoe werkt deze functie?' of 'Wat doet dit component?'. Wees specifiek in je vragen, dan krijg je betere antwoorden.
**Tweede: Inline Edit (Ctrl+K).** Je selecteert code, geeft een instructie zoals 'Zet dit om naar Tailwind classes', en de AI edit precies dat stukje. Super handig voor kleine aanpassingen.
**Derde: Composer (Ctrl+I).** Dit is voor grotere taken. Je kunt zeggen 'Voeg een product pagina toe met filtering en cart functionaliteit', en de AI genereert meerdere files tegelijk. Echt krachtig.
**Vierde: Tab Completion.** De AI voorspelt je code. Je begint te typen, en het suggereert wat je waarschijnlijk wilt. Je drukt Tab en klaar. Dit kost GEEN requests het is gratis!
**Vijfde: @mentions.** Je kunt context geven door @file, @folder, @web, en @docs te gebruiken. @docs is echt belangrijk: je kunt framework documentatie inladen, zodat de AI tegen de juiste versie van React of Next.js praat.
Nu: pricing. Het Hobby plan is gratis en volstaat voor het meeste. Je krijgt beperkte requests precies genoeg om te oefenen. Als je Student ben, kun je naar cursor.com/students gaan en upgraden naar Student, ook gratis, met veel meer requests."
_[Toon de vijf features op je vingers. Zeg "GRATIS" luidop voor Tab Completion veel studenten snappen niet dat dit geen request kost.]_
---
## BLOK 2: INSTALLATIE & SETUP (20 min)
### Slide 5: Download en installatie
**[09:10 - 09:14]**
"Goed, laten we beginnen met installatie. Je gaat naar cursor.com en klikt op Download. Cursor draait op Mac, Windows, en Linux dus voor iedereen. De installer is heel eenvoudig: je downloadt het, opent het, sleep je Cursor naar je Applications folder, en klaar. Als je op Windows bent, run je de .exe en volg je de stappen.
Dit kost ongeveer twee minuten. Als je al VS Code hebt, hoef je niks te desinstalleren Cursor en VS Code kunnen naast elkaar bestaan zonder problemen."
_[Navigeer naar cursor.com en wijs de download knop aan. Laat zien hoe het eruit ziet op Mac en Windows (screenshot).]_
---
### Slide 6: Account aanmaken en upgraden
**[09:14 - 09:18]**
"Zodra Cursor open is, maak je een account aan. Je hoeft NIET direct Student plan te nemen begin met Hobby. Je geeft je emailadres en een wachtwoord in. Cursor stuurt je een bevestigingsmail.
BELANGRIJK: als je Student bent, upgrade je NA deze eerste setup. Je gaat naar cursor.com/students, logt in met diezelfde email, en kiest Student plan. Geen creditcard, geen gedoe je werkt gewoon mee met onze les en je bent gedekt.
Na login zie je je Hobby account. Dan gaan we gelijk verder met setup."
_[Toon account maken op je eigen machine. Zeg duidelijk: "Hobby EERST, Student plan LATER". CHECKPOINT: check rond of iedereen ingelogd is.]_
---
### Slide 7: Interface tour
**[09:18 - 09:22]**
"Oké, je bent ingelogd. Laten we de interface doornemen zodat je weet waar alles zit.
Links zie je de **Activity Bar** kleine iconen voor Files, Search, Source Control, en Run & Debug. Dit is hetzelfde als VS Code.
Daaronder zie je de **Sidebar** dit is waar je bestanden staan als je Files opent.
In het midden: de **Editor**. Dit is waar je code schrijft en bewerkt.
Aan de rechterkant en dit is NIEUW vergeleken met normale VS Code de **Chat Panel**. Dit open je met Ctrl+L. Hier stel je vragen over je code.
Onder de editor zie je de **Terminal** Ctrl+backtick, dus Ctrl+` hier voer je commands uit.
Het mooie is: ALLES zit in één window. Je hoeft niet tussen VS Code en ChatGPT heen en weer te springen."
_[Beweeg je cursor rond op de interface. Duid elke component aan. Open de Chat Panel met Ctrl+L, sluit het weer met Escape.]_
---
### Slide 8: Terminal setup check SAMEN
**[09:22 - 09:30]**
"Nu gaan we even checken of je setup klaar is. Dit doen we via de Terminal. Open Terminal met Ctrl+backtick dus Ctrl+`.
Je ziet prompt. We gaan drie commands checken:
**Eerste: node -v** dit geeft je Node.js versie. Je wilt versie 18 of hoger. Als je error ziet 'command not found', moet je Node.js installeren via nodejs.org.
**Tweede: git --version** dit geeft je Git versie. Versie 2.20 of hoger is prima.
**Derde: npm -v** dit is de Package Manager die met Node.js komt. Ook 7.0 of hoger.
**Vierde: npx -v** dit is ESSENTIEEL. npx gebruiken we straks om create-next-app te draaien. Als npx niet werkt, kun je geen nieuw project aanmaken. npx komt normaal mee met npm, dus als npm werkt, werkt npx ook. Maar even checken!
Wie errors ziet, geen paniek. Steek je hand op, ik help je. Dit duurt twee minuten per persoon."
_[Open je eigen Terminal. Typ de vier commands. Laat zien wat output eruitziet. Sta twee minuten stil en help studenten die problemen hebben.]_
_[CHECKPOINT: zorg dat iedereen node, git, npm EN npx werkend heeft. Dit is essentieel voor de rest van de les.]_
---
## BLOK 3: CURSOR FEATURES (15 min)
### Slide 9: Chat Panel - Vragen over je code
**[09:30 - 09:36]**
"Nu duiken we in de échte kracht van Cursor: de Features. Laten we beginnen met **Chat Panel**.
Je opent Chat met Ctrl+L. Ik open het nu even op mijn scherm. Je ziet een textbox waar je kunt typen. Het bijzondere: Cursor ziet je HELE project, niet gewoon het stukje code dat je hebt geselect.
Stel je voor: je hebt een Next.js project met 15 componenten. Je kunt in Chat vragen: 'Hoe werkt de authentication flow in dit project?' en de AI snapt je hele setup en geeft een ingewikkeld antwoord.
**Tips voor goede Chat vragen:**
- Wees specifiek. Zeg niet 'Fix dit' maar 'Zet alle inline styles om naar Tailwind classes en zorg dat de layout hetzelfde blijft.'
- Geef context. 'In de ProductCard component, de border is nu blauw, kan hij rood zijn en dikker?'
- Stel vervolgvragen. De AI onthoudt wat je eerder vroeg.
Chat kost één request per bericht. Hop voorzichtig je hebt beperkte requests op Hobby."
_[Open Chat (Ctrl+L). Typ een voorbeeld vraag. Laat zien hoe antwoord zich opent.]_
---
### Slide 10: Inline Edit - Snelle code changes
**[09:36 - 09:41]**
"Volgende feature: **Inline Edit (Ctrl+K)**. Dit is voor als je kleine stukken wilt aanpassen.
Stel je voor: je hebt een component met inline styles die lelijke style={{color: 'red', marginTop: '10px'}}. Je wilt dit naar Tailwind. Je selecteert die hele component code, drukt Ctrl+K, en je typt: 'Zet alle inline styles om naar Tailwind classes, keep the same styling.'
Cursor selecteert precies dat stukje, geeft een preview, en je ziet de diff. Je kunt dan een diff accepteren of afwijzen. Super handig voor refactoring.
**Voorbeeld in mijn hoofd:**
Input: Component met inline styles
Instructie: 'Convert to Tailwind'
Output: Dezelfde component, maar nu className='text-red-600 mt-2.5'
Dit kost ook één request. Gebruik het voor kleine, chirurgische aanpassingen."
_[Maak een voorbeeld component met inline styles. Demonstreer Ctrl+K en de AI vervanging.]_
---
### Slide 11: Composer - Multi-file grote taken
**[09:41 - 09:46]**
"Nu het heavyweight: **Composer (Ctrl+I)**. Dit is voor als je GROTE dingen wilt doen dingen die meerdere files raken.
Stel je voor: je hebt een Next.js app met één pagina. Je wilt een heel nieuwe feature: een Admin Dashboard met login, user list, en stats panel. Dit raakt misschien 5 bestanden: nieuwe route, nieuwe component, styles, database queries, alles.
In plaats van dit in stukken te vragen in Chat, openen je Composer met Ctrl+I. Je zegt: 'Voeg een Admin Dashboard toe met login, user management, en stats. Zet het op /admin route.'
Composer genereert ALLES tegelijk alle files, alle imports, alle routing. Je ziet een preview, klikt 'Accept', en BOEM je project heeft een werkende admin dashboard.
Dit is échte time-saver. En ja, dit kost ook één request niet meer, niet minder, want Cursor snapt dat je één logische taak doet."
_[Open je eigen project. Open Composer (Ctrl+I). Laat zien hoe je een multi-file taak kunt beschrijven.]_
---
### Slide 12: Tab Completion - AI code voorspelling
**[09:46 - 09:50]**
"Feature vier: **Tab Completion**. Dit is mijn favoriet. En het BELANGRIJKSTE: dit kost GEEN requests. Nul. Gratis.
Je begint gewoon code te typen. Terwijl je typt, ziet Cursor wat je waarschijnlijk wilt doen. Je typt:
```javascript
const handleClick = () => {
```
En Cursor suggereert al wat je waarschijnlijk volgende doet. Je drukt Tab en het wordt aanvaard. Je typt verder, Tab, verder, Tab. Dit gaat super snel.
**Waarom is dit gratis?** Omdat Tab Completion niet intensief is voor Cursor het is lokale voorspelling, niet een volledige AI analyse. Dus gebruik dit VEEL! Dit moet je reflexmatig doen.
**Tip:** als je een suggestion ziet die je niet wilt, druk je gewoon Escape of tik je wat anders. Geen probleem.
Dit is echt de feature waar je sneller in je code wordt als je het goed gebruikt."
_[Demonstreer Tab Completion in je editor. Typ langzaam zodat Cursor suggestions toont. Druk Tab en laat zien hoe snel dit gaat.]_
---
### Slide 13: @mentions - Context voor je vragen
**[09:50 - 09:55]**
"Laatste feature: **@mentions**. Dit is hoe je de AI context geeft.
Je kent het van Slack en Discord: als je @iemand typt, tag je ze. In Cursor Chat doe je iets soortgelijks.
Je kunt typen:
- **@file** je verwijst naar een specifiek bestand. Voorbeeld: '@pages/api/auth.js write a logout endpoint'
- **@folder** je verwijst naar een hele folder. Voorbeeld: '@components analyze this folder and tell me architecture'
- **@web** de AI zoekt het web op. Voorbeeld: 'how do I connect to @web stripe payment gateway?'
- **@docs** DIT IS HEEL BELANGRIJK je verwijst naar framework documentatie. Voorbeeld: '@docs latest react patterns for state management'
@docs is een game changer. Als je @docs docs react typen, zal de AI antwoorden tegen de officieële React documentatie, niet tegen oude tutorials. Dit voorkomt dat je advice krijgt van 2019.
We gaan straks je Skills opzetten, en daar laad je @docs in. Ongelofelijk powerful."
_[Typ in Chat: '@docs how do I use hooks?' Laat zien dat niks gebeurt totdat Skills ingesteld zijn dat doen we volgende slide.]_
---
## BLOK 4: SKILLS, RULES & REQUESTS (15 min)
### Slide 14: Cursor Skills - Framework documentatie inladen
**[09:55 - 10:05]**
"Oké, volgende stap. Ik zei net dat @docs belangrijk is. Daarvoor moeten we eerst je **Cursor Skills** instellen. Dit is hoe je documentatie inlaadt zodat de AI weet tegen welke versies het praat.
We doen dit SAMEN, dus volg mee op je eigen machine.
**Stap 1:** Ga naar Settings. Op Mac: Cmd+, (command komma). Op Windows: Ctrl+, (control komma). De Settings paneel opent.
**Stap 2:** Je zoekt naar 'Features' en je klikt op Features.
**Stap 3:** Je zoekt naar 'Docs' je ziet 'Cursor Docs' instelling. Daar klikt je op.
**Stap 4:** Je ziet een + knop. Je klikt erop. Nu kun je documentatie toevoegen. Dit zijn de drie die ik aanraad:
- **Next.js:** je gaat naar nextjs.org/docs en je pakt die URL. Voeg toe.
- **React:** react.dev is de officieële React docs. Voeg toe.
- **Tailwind CSS:** tailwindcss.com/docs. Voeg toe.
Na toevoegen kun je in Chat @docs gebruiken en de AI leest tegen jouw documentatie. Test het: type in Chat '@docs what is the latest way to do routing in next.js?' en je krijgt antwoord uit de officieële Next.js documentatie, niet uit random StackOverflow.
Dit is echt belangrijk voor professioneel werken."
_[DOEN, niet demo. Zorg dat iedereen dit meegedaan heeft. Help met technische vragen. CHECKPOINT: vraag "Iedereen Skills ingesteld?" en wacht op bevestiging.]_
---
### Slide 15: .cursorrules - Jouw AI richtlijnen
**[10:05 - 10:12]**
"Nu gaan we een .cursorrules bestand maken. Dit is NIET handmatig geschreven Cursor genereert dit voor je.
Een .cursorrules file is een bestand in de root van je project waarin je aan Cursor zegt: 'Hey, als je met mijn code werkt, volg deze richtlijnen.'
Voorbeeld richtlijnen:
- 'Alle componenten moeten TypeScript hebben type annotations'
- 'Gebruik Tailwind, geen CSS modules'
- 'Nederlandse comments in de code'
- 'Altijd error handling toevoegen'
Dit is SUPER handig omdat je je preferences maar één keer ingesteld hebt, en dan volgt de AI altijd.
**Hoe maak je het?** Heel makkelijk:
**Stap 1:** Je opent Chat (Ctrl+L).
**Stap 2:** Je plakt deze prompt:
'Genereer een .cursorrules bestand voor een Next.js + Tailwind CSS + TypeScript project. Include richtlijnen voor: code style, components best practices, error handling, en Nederlandse comments. Output het volledige bestand klaar om te kopieën.'
**Stap 3:** Cursor genereert een complete .cursorrules file. Je kopieert dit.
**Stap 4:** In je project maak je een nieuw bestand genaamd `.cursorrules` in de root folder. Je plakt de content en je saved.
**Klaar!** Dát is één request, en nu volgt Cursor altijd jouw rules.
Dit is echt tijd besparen. Je zegt nooit meer 'gebruik TypeScript' dat weet Cursor al."
_[Open Chat. Paste je eigen .cursorrules prompt (aanwezig op je machine). Laat zien hoe output eruitziet. Demonstreer kopieën naar nieuw bestand.]_
---
### Slide 16: Request management - Budget uitzetten
**[10:12 - 10:20]**
"Oké, de grote vraag: requests. Op Hobby heb je BEPERKT aantal requests. Je wilt ze slim gebruiken.
**Hier is de breakdown:**
- **Tab Completion:** GRATIS. Ongelimiteerd. Gebruik dit voor kleine dingen.
- **Chat:** 1 request per bericht. Wees voorzichtig.
- **Inline Edit:** 1 request. Snel maar denk je in voor je tikt.
- **Composer:** 1 request, ongeacht hoe groot. Dit is eigenlijk heel voordelig voor grote dingen.
**Budget tips:**
**Eén: Denk eerst, prompt daarna.** Voordat je iets vraagt, bepaal je wat je PRECIES wilt. Maak een kopje koffie, bedenk je vraag, tik dan. Dit voorkomt vijf mislukte requests.
**Twee: Combineer taken.** In plaats van drie aparte Chat vragen, maak je één grote vraag. Zeg alles in één bericht.
**Drie: Gebruik Tab Completion voor kleine dingen.** Import statements, losse variabelen, functies. Dit bespaart requests.
**Vier: Composer voor grote refactors.** Als je iets aan meerdere files moet doen, doe je het in Composer, niet in aparte Chat vragen.
**Vijf: @mentions voor efficiency.** @docs helpt je beter vragen stellen, minder 'wat is het beste patroon' vragen.
Op Hobby krijg je genoeg om deze hele les te doen en meer. Maar als je gaat echt projecten bouwen, upgrade je naar Student. Dan heb je veel meer."
_[Toon scherm met request counter. Leg uit waar je request status ziet (rechtsboven in Cursor).]_
---
## BLOK 5: LIVE DEMO (15 min)
### Slide 17: Live Demo - Van nul naar werkende website
**[10:20 - 10:35]**
"Oké, genoeg theorie. Ik bouw nu in real-time een werkende Next.js website. Dit doet twee dingen: één, je ziet hoe snel dit echt gaat, en twee, je ziet de volledige workflow die je straks zelf doet.
**Stap 1: Project setup**
```bash
mkdir my-demo-site
cd my-demo-site
git init
```
Ik heb een folder, git aan. Cursor openen.
**Stap 2: Next.js project**
```bash
npx create-next-app@latest . --typescript --tailwind --app
```
Ik zeg Yes op TypeScript, Yes op Tailwind, Yes op App Router. Dit duurt ongeveer een minuut.
**Stap 3: Dev server**
```bash
npm run dev
```
Nu draait http://localhost:3000 met de default Next.js pagina.
**Stap 4: .cursorrules via Chat**
Ctrl+L. Ik paste mijn prompt. Één request. Copy. Make file `.cursorrules`. Done.
**Stap 5: HeroSection via Composer**
Ctrl+I. 'Create a Hero section component in app/components/HeroSection.tsx with a headline, subheading, and a Call-to-Action button. Use Tailwind classes. Make it look modern.'
Cursor genereert de hele component. Één request. Ik accepteer.
**Stap 6: Integrate in page**
Ik open app/page.tsx. Voeg import HeroSection. Zet het in HTML. Refresh browser. BOOM. Werkende website, mooi design.
**Stap 7: Git commit**
```bash
git add .
git commit -m "Initial setup with HeroSection"
```
**Totaal:** Ik gebruikte 2 requests (één voor .cursorrules, één voor HeroSection). Tab Completion hielp met imports en minor edits. Van nul naar werkende website in 15 minuten. Dat kan jij straks ook."
_[ECHTE DEMO, niet gerendeert. Wys elke stap live. Stop niet totdat npm run dev werkt en browser toont mooi design. Timing is belangrijk studenten moeten zien dat dit ECHT snel gaat.]_
---
## BLOK 6: HANDS-ON PRAKTIJK (75 min + 15 min pauze)
### Slide 18: Hands-on opdracht - Studenten bouwen eigen project
**[10:35 - 10:50] (+ pauze 10:15-10:30, dan verder tot 11:30]**
"Jullie beurt. Hier is wat je gaat doen. Dit is dezelfde workflow als mijn demo, maar je doet het zelf.
**Opdracht:** Bouw een NIEUW project. Mag elk onderwerp zijn portfolio site, blog, product showcase, whatever. Belangrijkste: je volgt deze stappen:
**Stap 1: Project initialize**
```bash
mkdir my-awesome-project
cd my-awesome-project
git init
```
**Stap 2: Next.js setup**
```bash
npx create-next-app@latest . --typescript --tailwind --app
```
TypeScript = Yes, Tailwind = Yes, App Router = Yes.
**Stap 3: Cursor Skills check**
Zorg dat je Next.js, React, en Tailwind documentatie ingeladen hebt. Test @docs.
**Stap 4: .cursorrules genereren**
Chat, één prompt, één request. Kopieën naar bestand.
**Stap 5: Componenten bouwen**
Gebruik Composer (Ctrl+I) om componenten te genereren:
- HeroSection (headline, subheading, CTA)
- FeatureCards (3-4 feature cards met iconen en beschrijvingen)
- TestimonialSection (3 testimonials)
- Footer
Elke Composer request = één component.
**Stap 6: Integreren in main page**
Import alle componenten in app/page.tsx. npm run dev. Check dat alles looks good.
**Stap 7: Git commit**
```bash
git add .
git commit -m "Complete project setup with all components"
```
**Timing:**
- Nu tot 10:15: je begint. Ik loop rond, ik help met problemen.
- 10:15-10:30: PAUZE. Stretch, koffie, toilet.
- 10:30-11:15: jullie bouwen verder. Meer componenten, refinement, styling tweaks. Ik help.
**Checks tussendoor:**
- Is npm run dev werkend?
- Hoe veel requests heb je gebruikt?
- Zit je stuck? Vraag me.
**Let op:** dit is HANDS-ON. Je moet dit zelf doen, niet copy-paste van mij. De waarde zit in het leren van de workflow."
_[Zet timer op. Check na 10 minuten wie klaar is met setup. Help trackers. Zorg dat iedereen minstens HeroSection werkend heeft voor de pauze.]_
_[Na pauze: motiveer studenten, help met volgende componenten, praise progress.]_
---
## BLOK 7: AFSLUITING (15 min)
### Slide 19: Resultaten delen
**[11:15 - 11:25]**
"Goed werk iedereen! Nu gaan we een paar resultaten delen. Ik wil 3-4 van jullie zien wat je hebt gebouwd.
[Roep studenten op om hun project op scherm te delen. Laat hun website zien. Zeg wat je goed vindt 'Leuke kleuren!' of 'Mooie layout, hoe heb je die FeatureCards gedaan?' Geef high-fives digitaal of letterlijk.]
Dit is echt professioneel werk. En je hebt dit in anderhalf uur gebouwd. Besef je dat? Met OpenCode zou dit twee keer zo lang geduurd hebben."
_[Echt naar mensen luisteren. 3-4 korte demo's, niet lang. Maak het positief.]_
---
### Slide 20: Samenvatting - De les in vijf punten
**[11:25 - 11:35]**
"Laten we samenvatten wat je vandaag geleerd hebt.
**Eén: Setup.** Je kunt Cursor installeren, een account maken, Skills configureren. Dit is standaard, je doet dit nu automatisch.
**Twee: AI Features.** Je kent Chat, Inline Edit, Composer, Tab Completion, @mentions. Elke feature heeft een use case. Chat voor vragen, Composer voor grote dingen, Tab Completion voor snelheid.
**Drie: Workflow.** Initialize project, create Next.js app, .cursorrules, build components, integrate, commit. Dit is de bouwsteen voor alle projecten voortaan.
**Vier: Request Management.** Je weet dat je beperkt hebt, je plant je requests, je combineert taken. Je denkt, daarna prompt.
**Vijf: Speed.** Je bouwde een website in anderhalf uur. Dat gaat niet slimmer worden dat is de norm voortaan.
Dit zijn de fundamentals. Volgende les gaan we dieper: effectief prompting, hoe je betere vragen stelt, hoe je iteratief werkt met de AI. Dat is waar de echte magie gebeurt."
_[Zeg duidelijk, maak oogcontact. Dit is de afsluiting.]_
---
### Slide 21: Huiswerk - DEBUG CHALLENGE
**[11:35 - 11:40]**
"Huiswerk. Dit is een DEBUG CHALLENGE. Je gaat een zip bestand krijgen via Teams. Die zip bevat een half-gebouwde Next.js project EXPRES met fouten.
**Wat zit erin:**
- Missende dependencies (package.json incomplete)
- Syntax errors (typos in components)
- Inline styles die naar Tailwind moeten
- Import statements die broken zijn
- Misschien een API route die niet werkt
**Je taak:**
1. Download de zip
2. Extract em
3. Open in Cursor
4. USE Cursor om ALLE fouten te fixen. Niet handmatig repareren dat is het punt niet. Gebruik Chat, Inline Edit, Composer. Leer hoe je de AI gebruikt om bugs op te spotten en te fixen.
5. Als alles werkend is: npm run dev moet geen errors geven
6. Push naar je GitHub
**Due:** volgende les. Zorg dat je een werkend project hebt en een GitHub link.
Dit is echt oefenen. Debugging met AI is een skill. Je leert het nu."
_[Upload de zip naar Teams. Zeg de naam van het bestand luid.]_
---
### Slide 22: Volgende les preview
**[11:40 - 11:45]**
"Volgende les: **Effectief Prompting & Iteratief Werken**. We gaan dieper.
Vandaag maakte je basale prompts. Volgende keer leren we:
- Prompt engineering: hoe je vragen stelt zodat de AI beter begrijpt wat je wilt
- Iteratie: hoe je feedback geeft en verbeteringen doet, stap voor stap
- Context building: hoe je de AI steeds meer context geeft voor betere resultaten
- Real-world scenarios: fixing bugs, refactoring code, building complex features
Dit is waar development met AI echt wordt. Prompting is een skill, net als typen of debuggen. We gaan het trainen.
Tot volgende week!"
_[Smile. Dit is motiverend. Zeg "Goed gedaan vandaag" en bedank iedereen voor de aandacht.]_
---
---
## TIMING OVERVIEW
| Blok | Slides | Duratie | Totaal (min) |
|------|--------|---------|------|
| **BLOK 1: Welkom & Mededeling** | 1-4 | 10 min | 0-10 |
| **BLOK 2: Installatie & Setup** | 5-8 | 20 min | 10-30 |
| **BLOK 3: Cursor Features** | 9-13 | 15 min | 30-45 |
| **BLOK 4: Skills, Rules & Requests** | 14-16 | 15 min | 45-60 |
| **BLOK 5: Live Demo** | 17 | 15 min | 60-75 |
| **BLOK 6: Hands-on (incl. pauze)** | 18 | 75 min + 15 min pauze | 75-165 |
| | | Pauze: 10:15-10:30 | |
| **BLOK 7: Afsluiting** | 19-22 | 10 min | 165-175 |
| **TOTAAL** | 22 | **175 minuten (2h 55min)** | |
---
## EXTRA NOTES VOOR TIM
- **Checkpoints:** Slide 6, 8, 14 zorg dat iedereen op dezelfde plek is voor je verdergaat
- **Problemen:** Bij terminal setup kan het 5 minuten extra kosten. Plan dit in.
- **Live demo timing:** dit duurt 15 minuten ECHT. Niet sneller studenten moeten het zien gebeuren.
- **Hands-on pauze:** 10:15-10:30 is VAST. Studenten hebben dit nodig.
- **Request usage:** controleer rond 10:45 dat niemand meer dan 5 requests gebruikt heeft. Anders raken ze op.
- **Debugging hw:** zip bestand moet express simple bugs hebben (missing import, extra comma, inline style). Niet complex.
- **Energy:** rond minuut 120 zakt energie. Zet muziek op pauze, trek grappen, keep it real.

View File

@@ -0,0 +1,231 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R /F5 6 0 R /F6 9 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
>>
endobj
5 0 obj
<<
/BaseFont /ZapfDingbats /Name /F4 /Subtype /Type1 /Type /Font
>>
endobj
6 0 obj
<<
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F5 /Subtype /Type1 /Type /Font
>>
endobj
7 0 obj
<<
/Contents 19 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
8 0 obj
<<
/Contents 20 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
9 0 obj
<<
/BaseFont /Symbol /Name /F6 /Subtype /Type1 /Type /Font
>>
endobj
10 0 obj
<<
/Contents 21 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
11 0 obj
<<
/Contents 22 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
12 0 obj
<<
/Contents 23 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
13 0 obj
<<
/Contents 24 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
14 0 obj
<<
/Contents 25 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
15 0 obj
<<
/Contents 26 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
16 0 obj
<<
/PageMode /UseNone /Pages 18 0 R /Type /Catalog
>>
endobj
17 0 obj
<<
/Author (\(anonymous\)) /CreationDate (D:20260224202513+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260224202513+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
>>
endobj
18 0 obj
<<
/Count 8 /Kids [ 7 0 R 8 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R ] /Type /Pages
>>
endobj
19 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1236
>>
stream
Gau0Bd;mr#'Rf-pm_7(d&X"]e&oU?"h53h9d^n0L5HGZ#@KJHm@FFE,o\4_(C^1'>Ubb)CK!Fa/mQ'Iq8.6nq=RgmR!&tL^s-<]G&:=L*Lk&k=QU0f>_obNHM5N<<ht)X>>[gn"ma*q'p%W$P#d(TiJT1#baab4G"Kmn^41HQ:X6:u<(0CVCY$D>n&JcK,1"8aH!PnoM6^r]eJ9OjGaa5rQgajgU1qm>1Jm\f!CZmY9Z2D4d,q@/rp4R15TR9q(^Arl<5k'<'K#IcmURLu3=!`OkFSu=u$T=U1"I<1OZf;cOlj>Ar=e96?eQA8sX]VB]6o27Z"XR]na$#NUWQ#O-OfikH/86Rf%RDA-+;Ol)LQ!C7$8Mur?6aK&'\,J$"u/tQCG&6?7DI2^UlB8#_S%N:D33h#>ndDN$aho,d,L6`U&.>pkC\A=kNTT4MCm77Z<p[DQ60KDcE<$E8)3^;h&oGTq>P3"S2V#3C_8&Br!f0/ku2IUp^bO*HAWlKOY6Ckpg>2q2IGS8/Y49XY?flK@0Ig_#Wu7+dr4P3M5RbM[=!0M1('Wch`%[LatoOnC4mNjdZ@%_<_MZriqX9'guukM0XZ5^M<*/b7$mae8VQab<bA+9h2fD*/[,87XtDqeL>*4tmk6<q3'/'bTD`mmC>_3N]u109q5ri`XiD@i_^Q;C1VIK>[bEM7\"[WS,,C^p86*AnYJC*;G"X,DK\]qI036d(pO@AJ%U\T0\Hd]^`+Q)M5d!gErn+485D40gp6-Y+8'qE@KUK@;@UTj#!ImfgYZ[J)IL:WV:40)9<K[K55YLG[=pL^gB_2k^3&>Q_i7a8aAZ@Y!Y,dg1=u-7D:K3opI"fh*d4X*`C#LdDASJiCUoVMkVX(bZ:b\o?igO<4V!^8^6Q_&&ZMrS#&f8%[E=;co!h\L`JeitJGFLeLm,PdMc]9mtF7\`X[T$FUpl4W;<KjKb3@Ds4<;[e/W+5(K#t6kQ\p06Rg:=u34!g8kRkGg0E<b=tV.8]e"hJ9Kr)Gh%G<@l(F&\.JG`oq/H7EN&!GA<Nl68Ca!_/_mGe\KS!f^Y-r+#@pq6)dE?4=^?'-;4?(s_UhdS$<J&V_%h@FupPY>9,[ai>7U4L>s'&bYYnjI94B]S`J0S6NW-r#)\]S[()CK%iW&NU,*',CsUgSWcU%0A'$BMH<:fThOIFm1.URMsTR-0tR2X$JqUXRud%.#.&T#)bokD!k))JFFF4ST=9C~>endstream
endobj
20 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1186
>>
stream
Gau0BgMYb*&:O:SbQ<33".84VP[Ck)_qAQZYS14mYWuO%17bA*,t1k'G/&K]ZL%?ib=.$`O\$%<kF<D"LB5@Y<rJ*A!.P-u]k"o<">aGK8-j*@&@A$4XL?W5#gGAS8e@bV^hpSRI)UNU!5I6$JR9ulWhFZNV81n^.1B&\*A\"UL"So\Y0fEZ?[!X('b">G&BrrK,4&Vcn/Z&V3)ePOYGRnR5hs^GJ'`WL%rhnq)OKn9p]A9c,Bd/)fH\E?@>6L,mbg-+lh..kX]?&)2Ep0j<GKrS9s[5;\&RgB!jc[nL.N*)Am$mC\LeuL^\(?ReqS$lHFntec$s"iO;UH?%85GImC0A7k4sc87uJ#$BuZ#C7>8Jq-H82Zjti+AX:6.,&%OZ7#3WMn9&Z-sC:FePKVX8M:I3$\Q5#bYGh>QgCgLC#8\WnoHH:Qi-T>]6o^teB>+XhQ*$hq[f,G%=pM'bN4MBdAiQ,;<#N'SaM2F8GIm4ie:(<=jnV/F\m1]K(MkcjaC8&pC4db$CBd:QU]Ut%JMP&6UN0BSfSO.f]BnEtE)<]uPIS_PU_-RgQ(T)1\UG=@62ZV^uORmFpC<&?E5NXtbZffaP%59_%i(U9!LO;82Z_E+,M8KLEk[$(XqknDNI=%'M1AC83::NA`je\n+duh[Q1;7(EX2h^Y;4X]b$4C'c[DhCN=;\@FlKoX&O-3lH)UG+%:*%g4%$a>Y=dFqtlb[#4595X$.L%E.=-78>Q;MNupp\=N0If5aNE/'kIOr[TfBjnf'IJmoU?i1Yl"O>(lWqCTP2TVZJ4S0qTkoVG"P_+D]h5J#A>Ui(Ip)P"[OuK+LqO8nZ@Ea\7+p?^*^7MVptJ@sguCe;WV8Fn8XLm8!%qRe&?aF9PKJ#+L(!aP$@f1LH&4PT2=W]87NEJu>lf)V@a0mOClXm#o^GL4qh$r&GNs_8\TZN5/TsH^:IG.sd/WudF?N$Y4kL5iPZa<<CLO,a(-X*W/SsJ$_DF_dLnfsD;1r]P=#CoF!nB_:\^T2:8p<5GibiGqq*]HAdq5L6X5p@bCIODR>0m`#d%=qCDNq:e#sR2Imm7!&GVU1)Il+gNrq"T`0HLY38Cio,VB9)jDO/&+f=<O@XbrE+q$)bbkeU8d:)BHuMO/0`K[F99pW+G2UX.P2Ps*bgoU'43Eq6XO+8?$qB`/Gq*$eP~>endstream
endobj
21 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1317
>>
stream
GauHJ?!$#V&:N_Ce@snC[f)c8[i&5u3NJf?G(&<qL'AkGM+OK&^Ye;V0bHG2Shl;f+JGg6>ISKM2ZnqeXF?4s!8g+&5%DAfKJR#!8-gm,2!dBtfZ$ke6tjl^'8$"B.h(`L^L_&XYs7koD(Xp69X+3N'RU1Ql\)W?kdtrWB<q-acOM)1hf7u"i6K7KQpqkZ%K)@/ea[mHUhL!Vk17DF5>IEA_($c:-hT-G?X,&3Do1$*D8@N]"e,9QV"CSmJ1-:^YZD!)cs-E6ND%g3`l?^XRochFXZP)?=1YB/D1FDUfi;`M*TnpPCs:9([,eq^KHDOum3.-[P!12F'UH;7-3I0n),rMOZM0bc-kf+r*[,3j)uD9$_Oi/2V(j9XGZ@di=ftYunM,ks,*Dgho;UW!f]d>aC^XmFG?Y4B7g5(;P/,MuD%l]aXZs%$qYS5^d!\mU%8r`]<Q>nI[e`!EJ:+dE]:?SQH5X[=J6D8\nU>&lo8(`ji,s*'9pAIPJFm1g;"oP#JRh"I)Yb$"GFcl1,+p&)-_*d>Npr`!0`0:4AudG0B5+T]Oh9s(jc3ZXVH9lW8f225j3TMT4^S81O1qdmDE;WrJPN[m%E]Chr0ZXn9G7e&RGXLdP+!Uc;ZFbPgt\3rA:_Rd$%T3J!C-.?2.^P1#357493h6sQuQ@X"V(l/c;n(aP<=b$M">2?B-srbh>b)L?W=.\6SgT2MPr#b>aN`uTN]SK$(Msi<"A;Rs"$_N:0kDfh'+h3/6_$WWKUBV3Q9<TVrQBU3\OJuUrVT2Z':D/p0TYBQVk,7ZUsWO<\i&hpR-k#Eu[*mYQLi1%_GPmjpD#kA/7VWPF^rT=1pE.T?^<080hrW=P;"*O/#3tH_]Fd@lO\%=N6PWG?Z1P'St2oCB<CrJ9c_S&E:[FXcC2V/)Vj+R.,W>p[Ya#6rHibPCNlHNoYA7&ERmXER_P9K`d"f"6CW\."&dQS"k8`&Z/o,=*LIpFi-!M(W[f(X>Ol#RYMUb(FGZ!0[J"Nf!-sYh`a/^JdT@3ja5,1qS`KWoJQB=_EI8_>e'f5iMUn@Us<$uQPIA+^mY0DfbAec*1WW2rjj"r+,7R,\f8KA0eD6oA$`>3b]EYi>$7L(MJbqZN?'[G*aRNWFJL+IgemC_3-Wk0S@0j=![9aaf=s2HbRr@/aQqm/eOKHGO"I6upGCVm,@g[lq"5!4$%*]jI05,Ljje$h3Xef>SI0R\qi'_W8mU&GHg;%ZD:3n"P,e=IJW`j,Y^QiC!guPXkO*"E)J(TPVI^J!D'V:brUV&To=:i04OMUMQWMQsaW_9JjO!c?UaG2e~>endstream
endobj
22 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1397
>>
stream
GauHKhbW8l&:WfGR+nOE$Z>>+7oJRm30q6H$d3QYHd\lH+`Z7%)i#[Bp:n@^P(kl3<C#iX;O_nncM*;]hb5FXpncMZ1^!M'JGp>lPX-u)apEj]!mZCMLBH8S`'LD.k(7'"Gf"OL&'`&eV5$-q*d=TaJ4TQ7OH0oMo]$De.>cAu$HICTCD_I^+@7g!I3;3C55I<bj"Tf*^u86PfEN%5[!K#V%FmYLV'@.Ni=]t3KX=DE^[RNc?CDp%E47a/#8_"R>l>MTWL;QhWRa(oiOqu08TFDS2m]Rg1IE&kF'Y"6PT#E1b]WbP[:&LO_S*tX6hPf>"C6>lQ&L:d>mqJ^QGl>*'!;+@Q(2L/;6Qk(JA-gAoR`9jDa4Qs<Th@Wa@rV_&r+*b\j/#<`Y>T*^`OW#JR/Gr"eei`X^LZQ/F4lp=U-PGU0ZIdYMPIT9BbXpI*UpLd+PSYBpha]STd2B8G1jcm9_.'r-<MmB!gtbM`84QL(QI'#.]V!aFr?#XUVAR0[`%X.=?Uo>+6#k,&H(*]fK?5Y70EdCsM<R2^H?J'l*+m.S5:D\-q)=R:+KL0V;:ZJ*BN@s!:a,>bnum):'1/%TdW90@G9:kg&O<2Oo+cX/!W!8-n^e5G=#CM'>9g!-&\dC,&_VE#FE)TJ:A%.bs6d9M>djRh;%473;-VJYKn9O-IU>WT%Llf=TNYN,sW.?7)M?*^MNqD6Cm((ajL?0Ue4ea'!,(o\t-7SA=O)/5:GDJLL"b7ImB##a=LOP/^=_^SXR`?@(0uUW3r3("B)-&;E(#0[';q37\l)Mq>3-W:S%`X^#@!$KokN^?asFg_E@@/J7"3OM"bi\3PV>FBcn/="MRBb4pJYA"oSHRk1`rhk$4a,[+9/P:4l4mPO$cg,m1aSqm\H9b^>MVC2\Pj#CtTfd6E#U>`7=5O/KP9@NPZiJML&G#p]+*Aa=7./!_kN%$B\!a$UR*W<j+V?4B#$7o7/m1LkW;hI;ASQuYCX]A\r])*j;ko>V$r0#1*+NZa_"Z@X\85c@n^SBMg!Sl1=%)J.1>(pDR4N.=1B3'fF>]H2&M,O?(p4,`gg@nQ6Y-=;\&BNZa>^,'ZDtF_EfcCTf@Y-O3$6P[<n&FRI-&fULH270Jk%0b24.DS5A%FHq<2k:a-[%*Epe[6kM8>&R@@GEW&f#7\!ca^qH:jh[n88,p)3a9S^u%W%<C7h\KJXPbDA,/Z+*R89rkA3Y`<Wm<$nHAtSden'NSE5NTe>a[A[RR\r\c4!PdU5B0C[S7>G@T6#Y)olbcoW4V6\"ABcX8"%jftGH:Ph$^45,m,f_fB?o'2N*2">!Ou6^pM,X)Q8>(1#$ZPWI.88d]paS__Mf[XoeWTn0lK:0T2.Ko['4[p4i/_iIU`o4RCiJl.B4%\"~>endstream
endobj
23 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1209
>>
stream
Gau0B9lo;R&A@ZcHKR\.M6LMn[SsSh$k&>"11;s"RM-LV.)`s1Lb4e3r;(8]0d"4s%U5r"\@^;VpXJID"U>9<r5KVi[g9PI$C!jK#a>u4d4P!cek:p%Q6IHS?jqG7,lqX"/o0;3do3I$^U2?@Xg\W[b(']IFa(WO3d*;l0o?:EUW^h"E%dmagCt8)8t(%!@+=W/Co0j*^ka]e*>RjNY"CK24+l`7U9?K#qn97j^`ug']I?l)r*$iM/)(ArmKVDdn4L&EC$1C^es;R<r[m82W,sQ2m@GcT^TVi^[%[BE0H9JcWrumYWtFqk6P(aPNt^Z1)hb<>mUKjQPEQ<`8Q6#prZ/Yo*6.9)!^/=f]/Uk%,7"e][c]IfQ[6G1Vq/Vi8Ed=?;nuN=_SoYsh[pW!DO5VM(PIH=-K*[.@BMbB7Ak=q2=aj\6UDlC=kCO\"2844n9t"75#Jo3A<Z9fcIdnfL^HaWoD-nV_mXj*4E+Rc-P#fsn!IaHGgFL@^O2Ag$;0E\455t<\:iK]?*82MR53\eCo%:2ku986?U,7i*:LAZoQ)cKl[VQc8o[bXdZ@J9m@UCH5@sDc?rA8[N5APl3eL*j,hChk1b,CmcNloo8`,\Y9]27<'/1?2.j&9c\5J%#7"^nt-t:?Vj@^^:LeTk$-huFF"<Vf7Pi&(XNJu+I(YK;3\Wg5%</100n=YXaIPUDmd]psHX?^LVdj1P:fr59aYV^tp;=c^Mh0?JeV(0UK@A8A"o@,jUVh9GZVkMZGc[;3*"?nD!.%IsealLQcT[[s*;cJbj\]Da1SW"Ko,\::\Q-+;62[p(8hb^dA@t\mf2g$;3n65iV(#@%m<\s@C`pltj(92b6LI/bHdQcN;OT:;Z'3&%h>)stu#D1oK'C>&?Zh;gKCpjEe"kA19X6@J7]\*);`3F09?$P#:]`=ofEajWe78=<i"1(<Y.no_0TiGS$3Y%<miWoI<<s:dWW:)JX=Kj\KPSPN7PTf`If?TCh]2[;k348s#"eejSa,d7>37\kWQKQUk+>q@P)&4NA#S)X5\Ce?Y@OIG,S@L_"OVO.pX+'O"a7/pXZ>[/>8Y.&c.Bk\;GQZi&0uXI"b-Q[4bBh';WN_&6%3-![?jK9L:&orWbP&fAKl&)e<TgWe)@3BlMm!MIlUp="npHhBE&K>E/V]2(j5'4V2-fC&.cVnYe"uQSU<nJ#&L4[^"7biV1&~>endstream
endobj
24 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1234
>>
stream
Gau`RD/\-!&H9tYQrO%BZsr'D[2nLRV3E7(8P#d5pOs+]ZN2H3-%<D:rqi1m_$(_[-^C9b=F4[npN`?uSM:9aJ$f-7jo?Jj34khK)2+&9"VD;P6RQ]_2!AtVnd/BViTLs6q[D:!pn4!%JM?3m8V%!q&@b4kBdBOE<6EK`n,gHifC^<;nG!JP[6InsT$-WAnm"W]jgd@c3Z.TiFQjgk_>+_PmQd;[AN,9cn)gJdSjN2TiB-@Lru,@'Hor`,i`GMPeb5>a>?WOjX-Ln_o'B6u[tIod4hD7EmnQbIfj<f+W7-$^$th85g3G`+rtWhP4WhH=3]MgQJTd/f-9^8EnL@Ed$0=;%ai($`rM;d]Bu>L!Oig`F7jU9!<RG7+;fJ@"8PB#_^t^$m-Nd&(g1O>4Bn`:UP".iHAK[47W<+')lhLVRL"`YK7r[>%,gKgX*Wt*=?XarP$@YYTU4+`2aNWg,2!=A*Kj:N:2)%_Dn>M+fJ]FF3@1o-lfphpqE0!i\dp6Z1Bi*QFPgBN1\&`%^XCmp;2oFT1>fQS>0g`tt#'9p),+J#HBG#L-o)cI\MXYn.5A%jEbC41u:FdH['Z6t[*^:sq7;AE9KIP8Q'VeeNiBq471t'.$E\<jL=%g*iC'Y_+/;ch<)2V1cM\C1_e1R\"UeBWFQ4`IhSD1]H(*eF1#Kng%NAUeb<?;*^"5N1VToT>_3&g*2s6BQLR8'pPAU.HIH*"@N]W"[h`Vo0T_+$jVc8]e3qZ7JOFsV]V.DS-dmH@0sZ'K5uXS&D:^/Q7'Y$K4d'a`n$UT/30oaP:nZpm^ub'%.#3Wa-*L"a+Rjh&EsC2GSETRG@)J%k5kDaW:Qg1IEpU8rT\nq2&Y4!`l$;o_M<j.\8'X,$_UXae'&[JIYU6`9s`dta!t5Q:$)T'Y_.'WVHTC1m44(>mPhUdFiaoE5d_!SV)AM`B;&gu,lt3Q;Hi17_mrSDgMO^lq6u?4*7bfu`/oDt6guf1<Q+I>)nLD8l*c&smp/(@a1s-(kn]7jWTeNjX>Zg"p5&_XgIAM="Z;Q31cHLWb,sm!^tJG!1L:j2[29MKUqH=UL]S2X$<&Q@Hf?#s?6-kR:VA<PW!nJ]-4Dn-@CY([?hOmntsi#jB1Y6lG'o/<kg0ZJBbl's+FPDLEN9lQD':98F]CQ!W,QSSJpoT>MCm_dq"u!klQ4*CeKt#$<_?[eJ.&.l5RU1c8lG:#6=,p^IXH%3%/'"Z1_aXT~>endstream
endobj
25 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 941
>>
stream
Gaua=?#u_o'Sc)T'k_l)GK6'R&qW(@['d6C[U!OL]V-/W"_%Merqnn[ldW'I]lA4_JI*LAo=g!$&dB%'<o1^si,Stf)[;8=BF;s0/r]YR*AsE-D"FK&`0$RB-R9;JpMa6p@:-dm\,FV^iX>1d%.G^2FRU_bO%RK*N>/lCkrO\-JI=7M1(D_$jfF,T-DV0*8a7`@Li'/4*2:0NM%^.3-&=1hYK/;>o4f<\*B<:$%Bhfs(,4-TZ!+u==Vj+\KCap+Eu=,MJh6JV;]%)oo\IZ<fIKNc8R/+e+j!&rKJCG^5CP#k-P2YlY->dFIU3>h_ZW_hmhg:5EBHkD7Q$Wq=/E*K;qbX`8YS,:pn/ShPAE#njYsAuh0sPJQef^Mh9XL%NhAPF_BFkYpRiKrD#9hD[-[b3G?+I!/@=Ih.GHfL::obT.%`_>_B@:m='0$GTu-qD3Y-l-eAN"kF91XkXYqGVHH0X1>%bCfi:f!aj1KeWs-T2q42OY^,)Ba2ZUlaGMa9N^-4*>jC`R:!37FkP/6chb9n[g`_2]!#.nZQ^#\(Rh1<I\73\>H2\5-`8q?7hKF)8^Of_SZL$aYH;KL%^ZH-pU[V6ti6J+Su9N=+uW&f!=KB]cTaZAq%nQ9=Hq[Eam`O/'Vt1g<Jni`duQ=$E<!oa_c\:46$k\,TH,HenZa%#3AX[rqA.H%JR5%Oki'Hgff5K5CnhY.%L<+j>%)kZ3RF<@bj*Cl1e`FDBWC%f>7ST>N:*)9,q&6hPH%_^,^"m]A-'b9Phoe2CJ`0Q&3AX3V/P5K9T(mPFHf-)gun'V3:sapUb77G?I3#ds4WgDi]i<E[$r]2T"8\rA:]R4g:^`5NCF^nZRUpk!oL6LmjN\:e=eb2YmrO/9YV5VGYUH#&eaE&GahE*g<ss$6K2rnKfcfNi919O,9D%q\ZWJG4U8)Lhe(j!u\~>endstream
endobj
26 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1266
>>
stream
Gb!#[D/\/e&H88.E@?RYCD!#28;bR!2G9C0=hU]dL&hH9<MCLFQK7>-)>`ZCP[r*YR\U@E#6?`Yj4ukBcU76@JE"Q5h_5(UmDJQ*"3/0L!u"(QIg]?Wg#k0BY#LiLMK<`j8$pbU\tPD?epMIl!SA`r#!]qK*#Pg<BcCoPXqJgV'I*,Pk!?4&0>7Vi!W3`ld<iNd,id1sA8EXHoSqR!rfKiRVqYQ=J]iBRrW1[l*Xot&*^!DA;&VuW'bk3jPQZF@_Wbp-<e"Gkl7mmOS@e8`S4;1AE<0JN?aDRE3<]/P8k`F^(8eH3,XV]:fMT9O2YN8=c6DjaP"!pL'X5`d:J6=rqHL(TZ^(&7dCQOIm]A4'C@tUHo*!A13UFP0?Frq'K9]'!@dfZ,]jBkn'^%/%qAoG`VCl:LMCoC7p,;Va**cNiOl\!mTU]>#aXSf;)n?7j`oJGAYo+[4WLT[8s/kqRZo!&C8pH185?F^1$cl?[el@ec9;XulAlX`ci!t_If$IAeqQ&P3BVrJg*`KbWO6c?1EB-MF6M%<Is,jt/JH_db%[C#pm@*"j@84aO:]dXCM$o+IdY81)#ftmQ[uZW:"TQa\Ga=_k5]&[MDh:eR\@bc1JoN!1EkC[uit/5UZ?@G!iiWE9gJ^WI`8_p8>P?M!9e'_H0lT;8\<L_B(X-5o:jY&Ff;sk@$)j']bh40Fj!iQ*,>"*.=22J@]t4(N5$ZG%r6(`1%m)Wj,3GIeW(RZo-OKC/QJb>Mf5@@ASt\1Z+I"!P1CqbOd8iXt<sdK]f$s6"r>,s0eTrs:DL'pI]g`to3YE@g<a'=AGLWnj].k`V'iXARONu5X*)PAQRNDqMbSL&.6g[lU`hl\kbf,-eHK&YDgS4qL:nLFP=WB&M'%223(-MEd,&_gH]aTo[9oM:A-MI5NpUEdh3hUW]%)e?1o;E.uZuW6?KM,;i!+($2I)p;Ofq$meD^,n7R8b6cTiJ.NfNYKOY[RUcJAnYBbaK!l11.c.*ROZs_b?eS5\Z'aoEdpf7te]hXg=OZXIB6$SJW]:eVt3H\S=6.2sE(m%9u>ApQ3/KBq=181qi:Sl,5!4V/DCIHbmb/jI88gdpP1\8$.uFOK%.7R6!&pA?$RoZWrG:.ej/?osZbhS.4CJT<\J'[\'>p*Q%RDeIEJA@_.u8M81h_LMJ(FS]%muiWHWU:@WN,(PFR.eu:%+ptb=B^ib`.`VD'=fdb-sn@i^&f\ar&^4GTd@>aP<bM'/p++jab9$gcZiTr4cO!+&~>endstream
endobj
xref
0 27
0000000000 65535 f
0000000061 00000 n
0000000142 00000 n
0000000249 00000 n
0000000361 00000 n
0000000476 00000 n
0000000559 00000 n
0000000664 00000 n
0000000869 00000 n
0000001074 00000 n
0000001151 00000 n
0000001357 00000 n
0000001563 00000 n
0000001769 00000 n
0000001975 00000 n
0000002181 00000 n
0000002387 00000 n
0000002457 00000 n
0000002738 00000 n
0000002846 00000 n
0000004174 00000 n
0000005452 00000 n
0000006861 00000 n
0000008350 00000 n
0000009651 00000 n
0000010977 00000 n
0000012009 00000 n
trailer
<<
/ID
[<d99cd050eea65d365d48b1f6682b5622><d99cd050eea65d365d48b1f6682b5622>]
% ReportLab generated PDF document -- digest (opensource)
/Info 17 0 R
/Root 16 0 R
/Size 27
>>
startxref
13367
%%EOF

View File

@@ -0,0 +1,231 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R /F5 6 0 R /F6 9 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
>>
endobj
5 0 obj
<<
/BaseFont /ZapfDingbats /Name /F4 /Subtype /Type1 /Type /Font
>>
endobj
6 0 obj
<<
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F5 /Subtype /Type1 /Type /Font
>>
endobj
7 0 obj
<<
/Contents 19 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
8 0 obj
<<
/Contents 20 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
9 0 obj
<<
/BaseFont /Symbol /Name /F6 /Subtype /Type1 /Type /Font
>>
endobj
10 0 obj
<<
/Contents 21 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
11 0 obj
<<
/Contents 22 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
12 0 obj
<<
/Contents 23 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
13 0 obj
<<
/Contents 24 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
14 0 obj
<<
/Contents 25 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
15 0 obj
<<
/Contents 26 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 18 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
16 0 obj
<<
/PageMode /UseNone /Pages 18 0 R /Type /Catalog
>>
endobj
17 0 obj
<<
/Author (\(anonymous\)) /CreationDate (D:20260224202513+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260224202513+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
>>
endobj
18 0 obj
<<
/Count 8 /Kids [ 7 0 R 8 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R ] /Type /Pages
>>
endobj
19 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1071
>>
stream
Gau`Qa`?,q&A@B[H>]19N;t(,'#(0SaHLU(ZCS4_9.l//;iiG)#3dG5q=nZCZR)*)aXKZ9"egM8If1<+$n+<@q(C4u7gZgkA\^8Nf>%M$OIX3>RG:P>[o13kOISZP"=+aF`,1^=fBeG*p=flcZ/eafK_%`f%#En8?ker'0I.UCfU%]$0a/%Lp*lXZ9L*V2`DNRQ6'B(V>ZVIV_#$2l4_T%<DDIP2QM$l3X<;e@5.6bep/eG0-t?$35fbY_189F"(D)B<5laS:7;NN'Jl'Dj"=O'#>n4ouZm`<+21.ijE,$G&WGB"82e?91fXPsu(\JjPY&,P]\3^pnR'Bq!a<lfH*^CR@hN"g>k]%^bWa[Gs*<NOV+dDaoO-7=Crc&jZhd@1dOu*B[>4b4;EQgjQKgn,q$2'U'R..FVd&hnFMUtS-c5YbI#IJb]mVA)"KsCKIWP_Yj.=D+5aM@fqGY&29#J)e+QYH#=o?,)/nuo=`a*4[9?*i#sqjEKiD9L/afhuJ:^3%qDgk/#,[BXGN#=kqEW3kl9)i2'D+\Y:_7$/BN8qGti:aNdQ5@eGsD$+L7,!eVZX-Pd..2[<hSds)1,]UlS1K)SlADM.aB):UCJ\M]RC^)7'Zh_3b@'?@Fi]m[p-lbMXqsC1,H<V)'H\_kd2+^Qh1u('sb%oAXK!D_l[4/6/7=/]UXrhGbWqrJXi5<>]j=./eGfo!V]YgjJDQ6FqE.IDbH.8.mTPQQ2Y2iB"kAi-L6>KXEJ9J);eI[0?l:sCB"m&idBo\''oFGjuf'=EXqKmdW2GG2;W@8mIc!XE&HZZ:$T0E/:g/O?B3LDZ.W%3o^di3.$^C,ZHmYr=f&%%k8K+TFJ18tt#^J1)fP/Bqc#B9M/ibWe.TKL!5cS#c(B(MX.PYk+$P%"i&*.)`<^Z\b3dq28<'?NVVH@%-MH+,jUURR"%o"c)QRe8foloPQ;*:.=P7I;c)(.E\>c&9kgT3CSBl+5RlZKoH/M+37]%_Z)&LuL!Q6AcFm8&':oG,LR$(*]Hdc6G8cHZ..^55u)\9,-`%=dDK<H(!fnBOK.aK?:l-aTj-~>endstream
endobj
20 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 855
>>
stream
Gau`Q?#SFN'Rf.Ggq?C_;I*XZLUV+%[1f8V9;ar>,pb1J5sc7;V#L3r-T%SM;>/@S=Mh>Hm@87cnI5PtL=Q<G"n3[iP7[j'Li3;/;\5!u)#*HE\6EYeP=>h^.Ul.78_YZ9X(X6Rd/ab."U>;&W]lMG#J+m0a[eQ5pTo&TIt+o.&(fM5@L[CnJK%s6Ot7VQ$fe"4k`XX#<+m.1`.7,\SBE7.f40pYO[>_t3$YC]b&S/?A-"m]1kK7g_Sbcg'5fjM6WGqj&-_,'"_n]JDn'os=;\Vg+S<,XS#%e"EU%JNZfC:4=Y>+\o8EaAb`$ZQXV>WrN)<=IYF#X8?B!Vq4\2ZDPa"j1VDVW`FVB6NPF\n*#)`mRDMp8'\fIXmgk4+YZ]ZsiF+k3.2oqY];5R&h,n8msdNQo#1W!mQOg%F_.Y3_\.>2QlQnX;',:5#*DT@HqUfODRA5n["\f0^S7:VB0L0X.i'BfAXq#(BS!6uq`1`d12L(ee>oR%Aa-]dI3!7(fnBl"_Kku8=G-.?Dj'mYOKdW4$/$`VCcJ5^8?8H$kP3)h'#9ZX-s@9l&B#lA7#0R+:Z#g=`SdK25`B-5p&p?ZcmKA%2:>G2dW=D/c'?;:ST)QP^a`Nu`UGmHjN;B3moThM;u-P,qZqG-g[aGU\FOd.(a2R"SQbDTA`/Dn7qqiVYnf>,OqAMU$K=mjc%([A:p4i&@DRCL4_-omKSg])W*_uN9if6!3aFS<-65"12tUlnc"g1@4_7ECs"1506Ab,(j/VCApAA#P!?o4,_aA97oNgT&!\a/$!\_!-#>rpn$hI%;%pFM=`\1cbSV#,aIm@QMj,ANb=$bSF=_@LkHjIKP4>l1Y~>endstream
endobj
21 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1260
>>
stream
Gau0C?#SIU'Re<2\F@CtX;rN`+-m?%@<3G7=L,*9cjcB>TLcH-7Nu,qG+9?Hn%4hGO#SlX,T?m]K3'8C4nm`!MLpU"D*IOui7AZ<cR;Toknn)!!_=tQO</g1U@"LK9`0]$kQV?M_q<#M0^:Mm4ZU@ck^(MkdV:e7f?>&cfoFCs!X]EI2)AKhOSIuhLUel^8f,-a!pO>gJ$c8*_!?iBWD>j<PMsY[`M4\``uW7R6Y$;$Q%np[b(99E9%:81'6qQq>S^T,K%Eb:;3W'3p^SkX,\*;)\(QGP*AV&2_FK(-Xen]0c?0.fQ>*9lQ5Pd9)bXK6!;TkM8_i5*Igb%A9M6WM>#t.Opfqem=<q=3=I*Wa^bN1uiG(VQ/'p(Vg%i@/UPIN`=;^G\g.%`VV2AEt5q6A5,E!W@e)pV.j3F'9?^L@d/.PBW8?>QhY2T=)Z`O#aKG_^ncmT=r#s+%CFo6M6C^HR'm'j"R#8l;jP:`F6\eQ7^h6#5"UZdV*\u8KU%&boA$3qiC7V3Bq>k<5QX9BH!bH5*PiX9I>W&NhL&i(tg2fr'4Y-E6E>L3_*$3eIm.3u*6_t@ZR<n`BfLr7hQh4qQqP")Jbm/ZE#02(V,Le9Kt,NAWUkI8M`l;/7=_m::t&<iL$aZfM(^:0-;fMDI*T.X^6(1,o&LT\66]`(N:-n];3pbh$^H7!OSdR5&Ogp'qi!<$fJ"mA/b@-:<Ljr/F\hD!N5^HpA/h\CNtat#N]I3,E9NAb=N6b>j?*u1ETJcgXL?E6(VT9F5HS@6ReC[nit[k.\(AcefC2uh9FhrsV?JQ(c6`>^QnE@4*u&p2f5Me#DlNk\\k5n>TDjVEU5^;PJEDH/c>6(3Z$%ZB\tLNV8chYkEeO,C9Gm?V2Y[C[C.2M%L8I3XDms8JsMJkX:VXO0@@Ap)?/Y[A570;Nkt;s:GtV2CT.5p;3ajN!Y^1i(<(gZ_l1&<GUffGO1BkLL11&bUFNk1H<O'PP?'%'i%!G&RfWoF.57G1^`cC4?P8H7@ok0flR6BcG>-?Y0$6D+CAZs$hp;f7OS$CNJA-0Q(N_,3i_I8o*$oU%X8ZPuuii5O,l!qJ,N7j>ihN5Y32H0tqj@6CX]#9_Q34`82j?]E[,0e509U-"4%!W[Y6`gKEoJ?ICrrRsY$q7fo,I?48(b\mCa(#sB9$3rgJU>&lkj)KnK+k4fQ^Kj4KN9[n8khl:CaZi%hYLp8T1rr1p]S1?^"jYoesbJ?EK$(e]Chgu&&rrOo$REG~>endstream
endobj
22 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 696
>>
stream
Gat=*>>O!-'RnB33"c`UU_quu4$8+^5XO5:7=3`]8DFbIj5Qkdrqe\^TXL(A=I'lioUPBuc]1Wu]1)SBC]SL;^tEo5KL.F!_@_Ra(9WhY.3Pgb66R[("j%uW'Hq?FQu.6>L59B)!LN/di@.#s#q@/bPe'Ef.&J_C1q![)2b,0J8;OE@+ClInLA@eNmFGJP;u:l;/-V@\U;0[sNIK(^@M4O4KLqOjN/@:Tq%Qco[C$1[`gub$;2G+rk_I4n:bIJ>E*jMQaF!UfP!tEsE"AX'Lm+=>.VfKCg3'7,61P#tBnne)MOSU>8E>BqU.E_<H7nN2#C[>\gQ!Mu>kKdd[*?0Uo=V/_F#^%Aq/SU6Rhn^7I>Y,]``$6"]M#TO_O=Jq?^f,>BAdH`]/@EW+(5`s2I2(@j"W3WHU&O>C\p3<b>mS]W(B:"1%dbucGQY!o0BO`#H+.E?_B,RRDVmfAQ+Q_e%tMjJmkFja=0W"LLcP$U9$"AgR:Ro9M>f5Ms2kT,N?8SRENcc=f#o3HX>qc\p[+1kRlVla6[6b[,l[`C$JaNX](k&LUIlgqic^9`s7LT%b9<^c`t+NGq=^IId=t3Oa;F5,>R%]V>tTgE>/M4YUbeUAV(DRN$I,iE&(4,FT^tMY#f3+XR2ccju1KP>PRC\0YiaAIrF9!'Y;ESNN%ZlYLIF='K5[s"TJKS<V-b~>endstream
endobj
23 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1075
>>
stream
Gau`R>u03/'RfGR\D%*9P@eXOG*9R50U3.T(u(cVPB]Y<dtT/gEDkH_lZ\ns=UU3m,g[7):9QnM3QKal@6j?kp$XjF3/ZYPQiZ4Yh#J*V5]dLRW-?j'bt&Q\:N;D35XZ0&SCYRo`I/&GF'hHB,ikZ72=.,?eC13k0#k[;M<@eUJCSW1*Z&r0%]f2AbZO%.,\ZaXI$ki';llB-jj=#C*F=:UJFCCZB?*5E&#/$lBHNsQLu,bC?T*J$8BL&3J_o&JWODhE2*%R2B4Ws)=sE7)de^3^%#H=<(t('P./uap@;*N`$29?\%X)/DU@02KkX/h>33D,".2$J`>AJ&c`c*.-)Q[m[!o-3+0t<c79WMnd3&o"'36nCuX9K`B'ImUM$@0F>*gLH9@;N$qj(^)R):6u)Ht:JhM&MCK;4:1rFoRj!lhq%Z7-aoU60LCF/1#d_`#!-Mk&HoB#AK<sKnco0S`t/t5\b`F24(=QV'o*FLU9U6-ITZ\[aNkVgH"9`B3[S`.PC"5g]QNErHCEa$p^HfKP1[`CT2F<co?PXZ%*A[B6!bSF^mQWP-RoXD/_/h9XT>r'&cVk#`XH+DY5HW8)gRQMo3afpn7$#l6Df9S\Rer8LJGJM".</'L<J-+VO/r&<A>nFWpaf.uF@tS42r3o>LkkHZ4.R5"(k[?>d#<*cg8h_!b`f4PKceiL?k1lU;Y)S#6Ejo@W,L>U8H;3S7Md>k4/%,a;%gPYnGG@F^$'pf&j+jDLh#LiQrV<*VYHZ?C/k3._c0LCMFn8JP^8W#l<#.-?h=G2Ajan&@p#A33EOP?Rr<d?rPZn%uq?52e.d_b#m5LE,1-Uq[,td%gC>i[em#%WFY*EJC=3<2R'_[p'STpaMl?OLl37=>nA%SbCHX=TN.+Ek7fpLG(u:SnuD\g]+q?^VUp&4WGXdk;uXk-sRgeSq"6A)1e!Y3h;<308uN"`3G##%5Vd;TAIJF`RYAOg3O,=kf$Z+7,Y5hYVkTd#`:Qi-O=;3YBQgmNeN*-A>f%+F;AY(q#)t><cF$g">2og5D)ljdH,Uf9XY45`=m=sO=S\NpD7IeB@d~>endstream
endobj
24 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 965
>>
stream
Gau`Rhf%4&&:WfG\@p9j1:EB+#qshd?q"YRNN>pgK]!'DM9VPegAU]Vn=s_[:qB88Z4rDH^AI?d]/qgA\CgYIj8]u.[!E,tLZfM@93d8Wd#Ar.)f0k!"N1nBKC=O?n,`iuTp2^V2Jo+=h+:?bJ10;'UhbZMV67oHN"[_<0'9$aLKMGdg3]f3')tW5<QYu2mN=Qm/:YRFa#[iT[I+U91:EE$PsmI;lL$t&[#LBXHO8,(@ge[I.GRKcV3QrUOkSK6/L9:9,iBlF+RPA>!lO7/^QK6h&D[#1E:1=Z!tt1O`Lrt6H@=$OU=s,8LW&^3%DYTG4[l3tg3D?:ED4d61I6V0U0^4foa/(?NIbd:LP,"hpH*IL]:GugIiQ.BBb+U,'%`u=6&-;,QN(5#>!;V&:@oII+/sk<=XHEfnlG:VbP8?=ncbn.Q_d[<WlMD'0lGp`^ps^,%b/f#cB\.aghqJnm3Y.(]@8Nu%rZ>B@:nP%Q12s>hsK![H657hlr[mIM6j`Ab-jYdL!gfDmckAsA$&G]&SiF-)s;o4WraN4(gRuSa/%BuHuagTOS[elV"E:n^FHAV36o;?V)+qo']i7?M]Gm;lgkeXM$'3@=Cj"ZB=2&+rm;,E$SjXqH&b+g7/--8.H.f)5<sA%S,u1U*apX^_\dHNA=V7MJRbTHFRGhT`r_l0B-[F0D>Rd!r%U[,O058WB\;\^-7:[1Rs>L;g^scsEkH&\reqhcSQAr6A'`#CXg2\,ju=:*iPrqQf%$Zrr$"?@7SE(hV3.Bb'>S;V5r>>?>3f/=WdZX9G"S6poq;P[I!4)]Q@RtPUG0Y1:YN3IJkanm&Xb\EX!3a=M0R&je]S+1fN-PW#LE)](9XjhB9j*qK`Yt>p#K%)"QE;6?uJ:;^+2<4R>5md=>[h=j0[ccWJ]uGAJR7[P'I?IUbW?K2gr1mC%WT(-JN?Ca/RKAL_nF2nJh41@lO~>endstream
endobj
25 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1016
>>
stream
Gaua<D/[lW&BE]&;]L-=_U)QBZ(5ED?n30`%/(C,Ue)t-gb(._4"#`ccQu:U\$hNp+W@=Pm.:%%s#Zhj:jcCBT9]J(1%Ytn0FmUaQllIi,M8'(2a,_pWf["5EsP0%65fX0$tj99lUF$Z0XKgS6AWga6eXR0)gD5_ETc(4cQN4q@-59J.13"*m&"oC%9t6_22o#5LedJC&f#'Z3i_d4W@Oo(#,TqkUE]_Fa)eJ3$DjmK8Q4q1'WP=YQ'=ZlFF[65cXXgpi,]tSqdj`q,>k7M&-</lchmd!C48GN@W7m2+I+,_Nu2]nLXRs4l4FnQ.3]m>dKnH&r1L]TT;1<P'uiWt\l(3hlCM=^'k5LX8N!*,=rU"i$GL5qnes9alR*'tS?>[XML^MEqY-8CbmSo-N4.]=U#LVk666ESTM[s.5*KPQPoq9"5\\kB_]$U#-$TOI@Z[hC@ZD6&.FtUjC@?-AkQK`a^O#MJ`>tm'U&'Af8B@&E\D_9'k,uMe<jOQGS]nEFJ@G'oAs5Q6Yr)sW`+1t!H/i4RHI*,"]B+@TV-6a;h=,5Fc\tN<9iX,Xi:GZkp*9FC3.o/V.(RHmS>&/pfhZHF[ROa,I6u$(7fXN6l1>moYqCphN0"KhgqqV-fp)89[PL(r:7G5O4(fQ\$M(2k>c*WbB9.T9dHXS!K&'VJV>[PkP$l2YQopS7a7I2/]]EdTBoho[SGc<!#:XgZMeUWLjMM:!MWD??a'dmV>X,_jC:Na;[4@iDX=u'/k=6?7eoK^=?^2t@I\.ut1];H"HdtGP`m.m+]#;>V@'XuJ]!H@[>RB/&#kND[Ao$IE_VD+p#c&!'n;O%$bC!ptmIfi=X$?7U><*ReKj*/e)-2a6pA3ZMn_=!qHcNa2YMf?JpadJ6LsO[pA4?`"6,9Bdh)-%W)q*IMM-tS_ie8gL5UG9ZP2ZJRG9@UgNXf)0P=XO5FG*9`4)d1$Wup&$gMuA=oEn5/<P^cWiu9tb,@.-IeeZ`NP@ToI"G42"U%]5uX]SF~>endstream
endobj
26 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 612
>>
stream
Gat=j95g:b&:j6K'm!&HFgLNWE_EE#Z0FM(U%Zo%&LKDqJJ;ps5JI</,!mERd7PC5SSr[8<m(j.1dpqo1Otr+0(/oK$/kslbqt(]\.*)#BAtn7K#/t9^f\S&TV=PpbqrVg$ns-$2?Hu'"&5/'4j,9ME_QkKNXGQ[1mA;/>S+7`a#V$,^,[``<kFf6\O52AYO"64bEJ;/8!3*8:)\bJ$9&1N)h1D,ikZLN6AHQNnf\R]XGt"6EHWAkdc;Cb57h/N^a4uk7rcVK&N>^J1X0"?F*5#aoDQG?)Tl2p\-f+`p_(OdE]^niF2>(7XZ[#Kf*+BUU7I&m0=9u:GNJP9_F)fuGd_pR9eljD+J[#LMCL&<KQuJQKZlT49S/Ii@5`_DVu)kEgGD^aG*RC?&[)7a7QukPRD^Eq_e90):DXWjAX;K+G2.fg^!F^eE>t+oA7HI9i,_qAA1[Ihr!7N2n6,Nq_h[(rK8n'AJgM:PaidE_MbI8&5mN.<Zq[+QlVj0,4J'Ga$i3@;0Wc?S!RTOK+?Bn9npj].EFJ&L5cZtN,Sd7.\DO47F&G1;:TH7.2`pe'<s8<pmDg47Mfhq:S"eOVK&<=\D*l$=$S_GgCn]OE~>endstream
endobj
xref
0 27
0000000000 65535 f
0000000061 00000 n
0000000142 00000 n
0000000249 00000 n
0000000361 00000 n
0000000476 00000 n
0000000559 00000 n
0000000664 00000 n
0000000869 00000 n
0000001074 00000 n
0000001151 00000 n
0000001357 00000 n
0000001563 00000 n
0000001769 00000 n
0000001975 00000 n
0000002181 00000 n
0000002387 00000 n
0000002457 00000 n
0000002738 00000 n
0000002846 00000 n
0000004009 00000 n
0000004955 00000 n
0000006307 00000 n
0000007094 00000 n
0000008261 00000 n
0000009317 00000 n
0000010425 00000 n
trailer
<<
/ID
[<e1d0fc053ad55e2642c670c80d245c9d><e1d0fc053ad55e2642c670c80d245c9d>]
% ReportLab generated PDF document -- digest (opensource)
/Info 17 0 R
/Root 16 0 R
/Size 27
>>
startxref
11128
%%EOF

View File

@@ -0,0 +1,240 @@
# Lesplan Les 3: Cursor - AI-Powered Code Editor
---
## Lesgegevens
| Item | Details |
|------|---------|
| **Les** | 3 van 18 |
| **Onderwerp** | Cursor - AI-Powered Code Editor |
| **Deel** | 1: AI Foundations (Les 1-4) |
| **Duur** | 3 uur (180 minuten) |
| **Spreektijd** | ~55 minuten |
| **Hands-on** | ~75 minuten |
| **Pauze** | 15 minuten (10:15-10:30) |
---
## Leerdoelen
Na deze les kan de student:
1. Uitleggen wat Cursor is en waarom het beter is dan OpenCode
2. Cursor installeren en een Hobby/Student account aanmaken
3. Cursor Skills/Docs installeren voor Next.js, React en Tailwind
4. Een .cursorrules bestand genereren met AI Chat
5. Een nieuw Next.js project opzetten met npx create-next-app
6. De vijf AI features gebruiken: Chat, Inline Edit, Composer, Tab Completion, @mentions
7. Efficiënt omgaan met requests (Hobby account management)
---
## Voorbereiding docent
### Technische setup
- [ ] Cursor geïnstalleerd
- [ ] Student account geactiveerd
- [ ] npx create-next-app getest
- [ ] Cursor Skills/Docs toegevoegd (Next.js, React, Tailwind)
- [ ] Demo .cursorrules gegenereerd
- [ ] Huiswerk zipbestand gemaakt (les3-debug-challenge.zip) met:
- Missende dependencies in package.json
- Syntax errors in componenten
- Inline styles die naar Tailwind moeten
- Missing imports
### Student voorkennis (uit Les 1-2)
- GitHub account, basis git
- npm/node.js geïnstalleerd
- Terminal basics
---
## Lesverloop
### Blok 1: Welkom & Mededeling (10 minuten)
| Tijd | Activiteit | Slides |
|------|------------|--------|
| 09:00-09:02 | Welkom, titelslide | 1 |
| 09:02-09:04 | Planning vandaag | 2 |
| 09:04-09:07 | Terugblik Les 2, waarom Cursor | 3 |
| 09:07-09:10 | Wat is Cursor? Features overzicht | 4 |
**Kernboodschap:** "Cursor is speciaal gemaakt voor developers. Geen token limits, ingebouwde terminal, gratis Student plan."
---
### Blok 2: Installatie & Setup (20 minuten)
| Tijd | Activiteit | Slides |
|------|------------|--------|
| 09:10-09:13 | Download Cursor | 5 |
| 09:13-09:18 | Account aanmaken (Hobby → Student later) | 6 |
| 09:18-09:22 | Interface tour | 7 |
| 09:22-09:30 | Terminal Setup Check SAMEN | 8 |
**Checkpoints:**
- ✓ Cursor geïnstalleerd en geopend
- ✓ node -v, git --version, npm -v werken
---
### Blok 3: Cursor Features (15 minuten)
| Tijd | Activiteit | Slides |
|------|------------|--------|
| 09:30-09:33 | Chat Panel (Ctrl+L) | 9 |
| 09:33-09:36 | Inline Edit (Ctrl+K) | 10 |
| 09:36-09:39 | Composer (Ctrl+I) | 11 |
| 09:39-09:41 | Tab Completion (GRATIS!) | 12 |
| 09:41-09:43 | @ Mentions + @docs | 13 |
**Kernboodschap:** "Vijf features, elk voor ander doel. Tab Completion is GRATIS - gebruik dat voor kleine dingen!"
---
### Blok 4: Skills, Rules & Requests (15 minuten)
| Tijd | Activiteit | Slides |
|------|------------|--------|
| 09:43-09:48 | Cursor Skills installeren (Next.js, React, Tailwind docs) - SAMEN DOEN | 14 |
| 09:48-09:53 | .cursorrules genereren met Chat | 15 |
| 09:53-09:58 | Request management tips (Hobby account) | 16 |
**Checkpoints:**
- ✓ Docs toegevoegd in Cursor Settings
- ✓ Studenten begrijpen request kosten
**Belangrijk:** Benadruk dat Tab Completion GRATIS is. Chat/Composer/Inline Edit kosten elk 1 request.
---
### Blok 5: Live Demo (15 minuten)
| Tijd | Activiteit | Slides |
|------|------------|--------|
| 09:58-10:13 | LIVE DEMO: Nieuw project workflow | 17 |
**Demo flow:**
1. mkdir + git init (1 min)
2. npx create-next-app@latest . (2 min)
3. npm run dev → toon localhost:3000 (1 min)
4. Chat: genereer .cursorrules (3 min, 1 request)
5. Composer: maak HeroSection component (4 min, 1 request)
6. Toon resultaat in browser (1 min)
7. git add + commit (1 min)
**Doel:** Studenten zien de hele workflow in 15 minuten
---
### Blok 6: Hands-On Opdracht (75 min + 15 min pauze)
#### Deel 1 (10:13-10:15, intro + start)
| Tijd | Activiteit |
|------|------------|
| 10:13-10:15 | Opdracht uitleggen (Slide 18 op scherm) |
#### Hands-on Deel 1 (start - 10:15)
Studenten starten met STAP 1 (npx create-next-app)
---
#### ☕ PAUZE (10:15-10:30)
---
#### Hands-on Deel 2 (10:30-11:45)
**Check [10:45]:**
"Wie heeft npx gedaan? Wie heeft .cursorrules? Wie heeft een component?"
**Check [11:00]:**
"Wie heeft al 2 componenten? Hoeveel requests heb je nog over?"
**Check [11:30]:**
"Wie heeft git gecommit? Wie wil zijn project laten zien?"
---
### Blok 7: Afsluiting (15 minuten)
| Tijd | Activiteit | Slides |
|------|------------|--------|
| 11:45-11:50 | Resultaten delen (3-4 studenten) | 19 |
| 11:50-11:53 | Samenvatting | 20 |
| 11:53-11:58 | Huiswerk uitleggen (DEBUG CHALLENGE!) | 21 |
| 11:58-12:00 | Volgende les preview | 22 |
---
## Materialen
- **Slide-Overzicht:** Les03-Slide-Overzicht.md
- **Lesopdracht:** Les03-Bijlage-A-Lesopdracht.md
- **Huiswerkopdracht:** Les03-Bijlage-B-Huiswerkopdracht.md
### Huiswerk zipbestand voorbereiden
**les3-debug-challenge.zip moet bevatten:**
- Een Next.js project (App Router, TypeScript, Tailwind)
- package.json met 1-2 missende dependencies (bijv. lucide-react gebruikt maar niet in deps)
- 1-2 syntax errors (missing closing tag, typo in import)
- 3-4 componenten met inline styles (style={{...}}) die naar Tailwind moeten
- Een component met een missing import
---
## Veelvoorkomende problemen
| Probleem | Oorzaak | Oplossing |
|----------|---------|-----------|
| npx create-next-app faalt | Node.js te oud | nvm install 20 && nvm use 20 |
| npm run dev werkt niet | Verkeerde folder | cd naar project folder, npm install |
| Chat/Composer reageert niet | Requests op of internet | Check cursor.com/account, check internet |
| .cursorrules werkt niet | Niet in root of typo | Check bestandsnaam en locatie |
| Cursor crash | Memory issue | Restart Cursor |
| Terminal commands not found | PATH niet goed | Restart Cursor, check nvm |
## Request Budget Planning
| Activiteit | Requests |
|------------|----------|
| .cursorrules genereren (Chat) | 1 |
| Hero component (Composer) | 1 |
| Styling tweaken (Inline Edit) | 1 |
| Extra component (Composer) | 1 |
| Vragen/debugging (Chat) | 2-3 |
| **Totaal in de les** | **~6-7** |
Tab Completion kost GEEN requests - benadruk dit!
---
## Voorbereiding Tim
**1-2 dagen voor les:**
- [ ] npx create-next-app testen op eigen machine
- [ ] Cursor Skills/Docs toevoegen
- [ ] .cursorrules genereren als demo
- [ ] Huiswerk zip maken met fouten
- [ ] Upload zip naar Teams
**Dag voor les:**
- [ ] Projector test
- [ ] Internet snelheid checken
- [ ] Cursor fresh install testen (wie upgrade snel?)
**Tijdens les:**
- [ ] Iedereen npx create-next-app laten draaien
- [ ] Terminal checks KRITISCH (slide 8)
- [ ] Request management benadrukken
- [ ] Active walking tijdens hands-on
---
**Succes met Les 3!**

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,6 @@
node_modules
.next
out
.DS_Store
*.tsbuildinfo
next-env.d.ts

View File

@@ -0,0 +1,103 @@
export default function AboutPage() {
return (
<div style={{
maxWidth: '800px',
margin: '0 auto',
padding: '40px 20px'
}}>
<h1 style={{
fontSize: '36px',
fontWeight: 'bold',
marginBottom: '24px',
color: '#1a1a1a'
}}>Over Ons</h1>
<p style={{
fontSize: '18px',
lineHeight: '1.8',
color: '#444',
marginBottom: '20px'
}}>
Wij zijn een team van gepassioneerde developers die geloven in de
kracht van AI-assisted development. Onze missie is om het bouwen
van websites sneller en toegankelijker te maken voor iedereen.
</p>
<div style={{
display: 'flex',
gap: '20px',
marginTop: '40px',
flexWrap: 'wrap'
}}>
<div style={{
flex: '1',
minWidth: '250px',
padding: '24px',
backgroundColor: '#f8f9fa',
borderRadius: '12px',
border: '1px solid #e0e0e0'
}}>
<h3 style={{
fontSize: '20px',
fontWeight: '600',
marginBottom: '12px',
color: '#333'
}}>Onze Visie</h3>
<p style={{
color: '#666',
lineHeight: '1.6'
}}>
AI maakt development niet makkelijker het maakt het SNELLER.
Je moet nog steeds begrijpen wat je bouwt.
</p>
</div>
<div style={{
flex: '1',
minWidth: '250px',
padding: '24px',
backgroundColor: '#f8f9fa',
borderRadius: '12px',
border: '1px solid #e0e0e0'
}}>
<h3 style={{
fontSize: '20px',
fontWeight: '600',
marginBottom: '12px',
color: '#333'
}}>Ons Team</h3>
<p style={{
color: '#666',
lineHeight: '1.6'
}}>
Vier developers, twee designers, en een AI die nooit slaapt.
Samen bouwen we de toekomst.
</p>
</div>
<div style={{
flex: '1',
minWidth: '250px',
padding: '24px',
backgroundColor: '#f8f9fa',
borderRadius: '12px',
border: '1px solid #e0e0e0'
}}>
<h3 style={{
fontSize: '20px',
fontWeight: '600',
marginBottom: '12px',
color: '#333'
}}>Contact</h3>
<p style={{
color: '#666',
lineHeight: '1.6'
}}>
Vragen? Neem contact op via ons contactformulier of stuur
een mail naar info@debugchallenge.nl.
</p>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,124 @@
"use client";
import { useState } from "react";
export default function ContactPage() {
const [naam, setNaam] = useState("");
const [email, setEmail] = useState("");
const [bericht, setBericht] = useState("");
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
alert(`Bedankt ${naam}! Je bericht is verzonden.`);
};
return (
<div style={{
maxWidth: '600px',
margin: '0 auto',
padding: '40px 20px'
}}>
<h1 style={{
fontSize: '36px',
fontWeight: 'bold',
marginBottom: '8px',
color: '#1a1a1a'
}}>Contact</h1>
<p style={{
color: '#666',
marginBottom: '32px',
fontSize: '16px'
}}>Heb je een vraag? Vul het formulier in!</p>
<form onSubmit={handleSubmit} style={{
display: 'flex',
flexDirection: 'column',
gap: '16px'
}}>
<div>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '500',
color: '#333'
}}>Naam</label>
<input
type="text"
value={naam}
onChange={(e) => setNaam(e.target.value)}
required
style={{
width: '100%',
padding: '10px 14px',
border: '1px solid #ccc',
borderRadius: '8px',
fontSize: '16px',
outline: 'none'
}}
/>
</div>
<div>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '500',
color: '#333'
}}>Email</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
style={{
width: '100%',
padding: '10px 14px',
border: '1px solid #ccc',
borderRadius: '8px',
fontSize: '16px',
outline: 'none'
}}
/>
</div>
<div>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '500',
color: '#333'
}}>Bericht</label>
<textarea
value={bericht}
onChange={(e) => setBericht(e.target.value)}
required
rows={5}
style={{
width: '100%',
padding: '10px 14px',
border: '1px solid #ccc',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
resize: 'vertical'
}}
/>
</div>
<button type="submit" style={{
backgroundColor: '#3b82f6',
color: 'white',
padding: '12px 24px',
border: 'none',
borderRadius: '8px',
fontSize: '16px',
fontWeight: '600',
cursor: 'pointer'
}}>
Verstuur Bericht
</button>
</form>
</div>
);
}

View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -0,0 +1,23 @@
import type { Metadata } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "Debug Challenge - Les 3",
description: "Fix alle fouten in dit project!",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="nl">
<body className="min-h-screen bg-white text-gray-900">
<Navbar />
{children}
<Footer />
</body>
</html>
);
}

View File

@@ -0,0 +1,12 @@
import Hero from "@/componenst/Hero";
import FeatureCards from "@/components/FeatureCards";
export default function Home() {
return (
<main>
<Hero />
<FeatureCards />
<TestimonialSection />
</main>
);
}

View File

@@ -0,0 +1,85 @@
import { Zap, Shield, Rocket } from "lucide-react"
interface Feature {
icon: React.ReactNode;
titel: string;
beschrijving: string;
}
const features: Feature[] = [
{
icon: <Zap size={32} />,
titel: "Supersnel",
beschrijving: "Bouw componenten in seconden met AI-powered code generation."
},
{
icon: <Shield size={32} />,
titel: "Betrouwbaar",
beschrijving: "TypeScript en ESLint zorgen voor foutloze, veilige code."
},
{
icon: <Rocket size={32} />,
titel: "Deploy Direct",
beschrijving: "Push naar GitHub en je site is live op Vercel binnen minuten."
}
];
export default function FeatureCards() {
return (
<section style={{
padding: '60px 20px',
backgroundColor: '#f9fafb',
}}>
<div style={{
maxWidth: '1000px',
margin: '0 auto'
}}>
<h2 style={{
fontSize: '32px',
fontWeight: 'bold',
textAlign: 'center',
marginBottom: '40px',
color: '#1a1a1a'
}}>Waarom Dit Project?</h2>
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: '24px'
}}>
{features.map((feature, index) => (
<div key={index} style={{
backgroundColor: 'white',
padding: '32px',
borderRadius: '16px',
border: '1px solid #e5e7eb',
textAlign: 'center',
boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
}}>
<div style={{
display: 'inline-flex',
padding: '12px',
backgroundColor: '#ede9fe',
borderRadius: '12px',
marginBottom: '16px',
color: '#7c3aed'
}}>
{feature.icon}
</div>
<h3 style={{
fontSize: '20px',
fontWeight: '600',
marginBottom: '8px',
color: '#1a1a1a'
}}>{feature.titel}</h3>
<p style={{
color: '#6b7280',
lineHeight: '1.6'
}}>{feature.beschrijving}</p>
</div>
))}
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,82 @@
import Link from "next/link"
export default function Footer() {
return (
<footer style={{
backgroundColor: '#1f2937',
color: 'white',
padding: '40px 20px',
marginTop: '60px'
}}>
<div style={{
maxWidth: '1000px',
margin: '0 auto',
display: 'flex',
justifyContent: 'space-between',
flexWrap: 'wrap',
gap: '32px'
}}>
<div>
<h3 style={{
fontSize: '18px',
fontWeight: '600',
marginBottom: '12px'
}}>🚀 Debug Challenge</h3>
<p style={{
color: '#9ca3af',
maxWidth: '300px',
lineHeight: '1.6',
fontSize: '14px'
}}>
Een project van NOVI Hogeschool om te leren
debuggen met Cursor AI.
</p>
</div>
<div>
<h3 style={{
fontSize: '18px',
fontWeight: '600',
marginBottom: '12px'
}}>Links</h3>
<div style={{
display: 'flex',
flexDirection: 'column',
gap: '8px'
}}>
<Link href="/" style={{ color: '#9ca3af', textDecoration: 'none', fontSize: '14px' }}>Home</Link>
<Link href="/about" style={{ color: '#9ca3af', textDecoration: 'none', fontSize: '14px' }}>Over Ons</Link>
<Link href="/contact" style={{ color: '#9ca3af', textDecoration: 'none', fontSize: '14px' }}>Contact</Link>
</div>
</div>
<div>
<h3 style={{
fontSize: '18px',
fontWeight: '600',
marginBottom: '12px'
}}>NOVI Hogeschool</h3>
<p style={{
color: '#9ca3af',
fontSize: '14px',
lineHeight: '1.6'
}}>
AI Development Cursus<br/>
Utrecht, Nederland
</p>
</div>
</div>
<div style={{
borderTop: '1px solid #374151',
marginTop: '32px',
paddingTop: '20px',
textAlign: 'center',
color: '#6b7280',
fontSize: '13px'
}}>
© 2025 Debug Challenge Les 3 Huiswerk
</div>
</footer>
)
}

View File

@@ -0,0 +1,53 @@
import { ArrowRight } from "lucide-react";
import Link from "next/link";
export default function Hero() {
return (
<section style={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
padding: '80px 20px',
textAlign: 'center',
color: 'white',
minHeight: '500px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}>
<div style={{
maxWidth: '700px'
}}>
<h1 style={{
fontSize: '48px',
fontWeight: 'bold',
marginBottom: '16px',
lineHeight: '1.2'
}}>
Bouw Sneller met AI
</h1>
<p style={{
fontSize: '20px',
marginBottom: '32px',
opacity: '0.9',
lineHeight: '1.6'
}}>
Ontdek hoe je met Cursor en Next.js in minuten een professionele
website bouwt. Van idee tot deployment in no-time.
</p>
<Link href="/about" style={{
display: 'inline-flex',
alignItems: 'center',
gap: '8px',
backgroundColor: 'white',
color: '#764ba2',
padding: '14px 28px',
borderRadius: '9999px',
fontWeight: '600',
fontSize: '16px',
textDecoration: 'none'
}}>
Meer Weten <ArrowRight size={18} />
</Link>
</div>
</section>
);
}

View File

@@ -0,0 +1,59 @@
"use client";
import Link from "next/link";
import { useState } from "react";
export default function Navbar() {
const [menuOpen, setMenuOpen] = useState(false);
return (
<nav style={{
backgroundColor: 'white',
borderBottom: '1px solid #e5e7eb',
padding: '0 20px',
position: 'sticky',
top: '0',
zIndex: '50'
}}>
<div style={{
maxWidth: '1200px',
margin: '0 auto',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
height: '64px'
}}>
<Link href="/" style={{
fontSize: '20px',
fontWeight: 'bold',
color: '#7c3aed',
textDecoration: 'none'
}}>
🚀 Debug Challenge
</Link>
<div style={{
display: 'flex',
gap: '24px',
alignItems: 'center'
}}>
<Link href="/" style={{
color: '#4b5563',
textDecoration: 'none',
fontWeight: '500'
}}>Home</Link>
<Link href="/about" style={{
color: '#4b5563',
textDecoration: 'none',
fontWeight: '500'
}}>Over Ons</Link>
<Link href="/contact" style={{
color: '#4b5563',
textDecoration: 'none',
fontWeight: '500'
}}>Contact</Link>
</div>
</div>
</nav>
);
}

View File

@@ -0,0 +1,73 @@
const testimonials = [
{
naam: "Lisa de Vries",
rol: "Frontend Developer",
tekst: "Cursor heeft mijn workflow compleet veranderd. Ik bouw nu in uren wat vroeger dagen kostte.",
},
{
naam: "Mark Jansen",
rol: "Student NOVI",
tekst: "Eindelijk een tool die écht begrijpt wat ik wil bouwen. De Composer feature is geweldig!",
},
{
naam: "Sophie Bakker",
rol: "Full-Stack Developer",
tekst: "Van OpenCode naar Cursor was de beste beslissing. Geen token limits meer, gewoon bouwen.",
},
];
export default function TestimonialSection() {
return (
<section style={{
padding: '60px 20px',
backgroundColor: 'white'
}}>
<div style={{
maxWidth: '1000px',
margin: '0 auto'
}}>
<h2 style={{
fontSize: '32px',
fontWeight: 'bold',
textAlign: 'center',
marginBottom: '40px',
color: '#1a1a1a'
}}>Wat Anderen Zeggen</h2>
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: '24px'
}}>
{testimonials.map((t, i) => (
<div key={i} style={{
padding: '28px',
backgroundColor: '#faf5ff',
borderRadius: '16px',
border: '1px solid #e9d5ff'
}}>
<p style={{
fontStyle: 'italic',
color: '#4b5563',
marginBottom: '16px',
lineHeight: '1.6',
fontSize: '15px'
}}>"{t.tekst}"</p>
<div>
<p style={{
fontWeight: '600',
color: '#1a1a1a',
fontSize: '15px'
}}>{t.naam}</p>
<p style={{
color: '#7c3aed',
fontSize: '13px'
}}>{t.rol}</p>
</div>
</div>
))}
</div>
</div>
</section>
)
}

View File

@@ -0,0 +1,4 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default nextConfig;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
{
"name": "les3-debug-challenge",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "14.2.5",
"react": "^18",
"react-dom": "^18"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"typescript": "^5",
"postcss": "^8",
"autoprefixer": "^10",
"tailwindcss": "^3.4.1",
"eslint": "^8",
"eslint-config-next": "14.2.5"
}
}

View File

@@ -0,0 +1,9 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
export default config;

View File

@@ -0,0 +1,13 @@
import type { Config } from "tailwindcss";
const config: Config = {
content: [
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {},
},
plugins: [],
};
export default config;

View File

@@ -0,0 +1,20 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [{ "name": "next" }],
"paths": { "@/*": ["./*"] }
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

Binary file not shown.