This commit is contained in:
2026-06-09 06:33:34 +02:00
parent 4f13aadc1a
commit dc284db9b6
172 changed files with 437 additions and 15151 deletions

View File

@@ -1,336 +0,0 @@
# QuickPoll Next.js Demo - Complete File Index
## 📍 START HERE
1. **For Tim (Teacher)**: Read `QUICK-START.md` first
2. **For Understanding the Project**: Read `README.md`
3. **For Complete Teaching Guide**: Read `TEACHER-GUIDE.md`
4. **For Technical Details**: Read `PROJECT-SUMMARY.md`
---
## 📦 Project Files
### Complete Project with Git History
```
quickpoll-demo-complete.zip ~160 KB
└─ Full project with all 10 git tags
Extract this and use git checkout to jump between steps
```
### Individual Step Archives
```
stap-0.zip ~32 KB Project setup + types + data
stap-1.zip ~32 KB Layout & Navigation
stap-2.zip ~33 KB Homepage with poll cards
stap-3.zip ~34 KB API routes (GET/POST)
stap-4.zip ~37 KB Vote API + demo form ⭐
stap-5.zip ~39 KB Poll detail page
stap-6.zip ~40 KB VoteForm component
stap-7.zip ~43 KB Error handling pages
stap-8.zip ~43 KB Middleware
bonus.zip ~44 KB Create poll page
```
**Total Project Size**: ~588 KB (all files)
---
## 📚 Documentation
### Quick Start (3 pages, 7.1 KB)
File: `QUICK-START.md`
- For Tim before class
- Setup instructions
- Key demo points
- Troubleshooting
- Recommended read time: 10 minutes
### Main README (3 pages, 7.2 KB)
File: `README.md`
- Project overview
- Getting started
- Key features
- Technology stack
- Common questions
### Complete Teacher Guide (5 pages, 8.5 KB)
File: `TEACHER-GUIDE.md`
- Complete lesson breakdown
- What to show in each step
- DevTools demonstration strategy
- Key concepts to teach
- Tips for live coding
### Project Summary (4 pages, 7.9 KB)
File: `PROJECT-SUMMARY.md`
- Technical architecture
- Git tag progression
- Teaching points
- Sample data
- Build information
---
## 🎓 Learning Path
### Beginner Path (Individual Zips)
If you want to start from scratch for each step:
```
1. Extract stap-0.zip
2. npm install && npm run dev
3. Do the live-coding
4. Extract stap-1.zip (new terminal)
5. Repeat...
```
**Pros**: Each step is fresh, you can code along
**Cons**: More setup, can't jump back easily
### Advanced Path (Complete Project)
If you want flexible jumping between steps:
```
1. Extract quickpoll-demo-complete.zip
2. npm install
3. npm run dev
4. git checkout stap-0 (or any step)
5. Browser auto-refreshes
6. git checkout stap-5 (jump forward)
```
**Pros**: One setup, jump anywhere, see git history
**Cons**: Less live-coding from scratch
**Recommended**: Use Complete Project with git checkout
---
## 📋 File Structure in Zips
Each zip contains a Next.js project with this structure:
```
quickpoll-demo/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── page.tsx # Homepage
│ │ ├── layout.tsx # Root layout with navbar
│ │ ├── globals.css # Tailwind imports
│ │ ├── loading.tsx # Loading skeleton
│ │ ├── error.tsx # Error boundary
│ │ ├── not-found.tsx # 404 page
│ │ ├── api/
│ │ │ └── polls/
│ │ │ ├── route.ts # GET all, POST create
│ │ │ └── [id]/
│ │ │ ├── route.ts # GET single poll
│ │ │ ├── vote/route.ts # POST vote ⭐
│ │ │ └── not-found.tsx
│ │ ├── poll/[id]/
│ │ │ ├── page.tsx # Dynamic detail page
│ │ │ └── not-found.tsx
│ │ └── create/
│ │ └── page.tsx # Create poll form (bonus)
│ ├── components/
│ │ └── VoteForm.tsx # Voting component
│ ├── lib/
│ │ └── data.ts # Mock data & functions
│ ├── types/
│ │ └── index.ts # TypeScript interfaces
│ └── middleware.ts # Request logging
├── .cursorrules # Code style guidelines
├── package.json
├── tsconfig.json
├── tailwind.config.ts
└── .gitignore
```
---
## ⭐ Key Files to Show Students
### Data & Types
- `src/types/index.ts` - Poll interface, VoteBody interface
- `src/lib/data.ts` - Sample polls, getPolls(), votePoll()
### API Routes
- `src/app/api/polls/route.ts` - GET all, POST create
- `src/app/api/polls/[id]/route.ts` - GET single
- `src/app/api/polls/[id]/vote/route.ts` - POST vote (Key demo point!)
### Pages
- `src/app/page.tsx` - Homepage with poll cards
- `src/app/poll/[id]/page.tsx` - Poll detail page with voting
### Components
- `src/components/VoteForm.tsx` - Client component with voting logic
### Layout & Styling
- `src/app/layout.tsx` - Navbar and global structure
- `src/app/globals.css` - Tailwind imports (minimal)
---
## 🔧 System Requirements
- **Node.js**: 18 or higher
- **npm**: 9 or higher
- **Disk Space**: ~500 MB for node_modules (generated during npm install)
- **Browser**: Any modern browser with DevTools (for demos)
- **Projector**: Recommended for classroom (test beforehand!)
---
## 🚀 Quick Commands Reference
### Setup
```bash
unzip quickpoll-demo-complete.zip
cd quickpoll-demo
npm install
npm run dev
```
### During Class
```bash
git tag -l # List all tags
git checkout stap-4 # Jump to step 4
git diff stap-2 stap-3 # See what changed
git log --oneline # Show commits
```
### Build & Test
```bash
npm run build # Production build
npm run dev -- -p 3001 # Different port
```
### Browser
```
http://localhost:3000 # Homepage
http://localhost:3000/poll/1 # Poll detail
http://localhost:3000/create # Create form (bonus)
http://localhost:3000/demo # Demo form (stap-4 only)
```
### DevTools (F12)
- Console: See console.log statements
- Network: See API requests
- Elements: Inspect HTML/CSS
---
## 📊 Project Statistics
| Metric | Value |
|--------|-------|
| Git Commits | 10 |
| Git Tags | 9 (stap-0 through stap-8, plus bonus) |
| TypeScript Files | 11 |
| Components | 2 (VoteForm + pages) |
| API Routes | 4 |
| Total Lines of Code | ~1,200 |
| Build Time | ~1.3 seconds |
| Estimated Teaching Time | 60-75 minutes |
---
## ✅ Verification Checklist
Before teaching, verify:
- [ ] All 10 zip files present and readable
- [ ] quickpoll-demo-complete.zip contains .git folder
- [ ] All 4 markdown documentation files present
- [ ] npm version 9+ installed
- [ ] Node 18+ installed
- [ ] Can extract and npm install without errors
- [ ] npm run dev starts without errors
- [ ] Homepage loads at http://localhost:3000
- [ ] All git tags present (`git tag -l`)
- [ ] Can checkout different tags without errors
- [ ] DevTools work correctly (F12)
- [ ] Network tab shows API requests
- [ ] Console.log appears in DevTools console
---
## 🎯 Recommended Usage
### For First-Time Teachers
1. Read QUICK-START.md (10 min)
2. Follow setup instructions
3. Test each step beforehand
4. Use git checkout to jump between steps
5. Use projector for display
### For Experienced Teachers
1. Extract complete project
2. Review TEACHER-GUIDE.md for key points
3. Customize as needed
4. Focus on stap-4 demo form
### For Students (After Class)
1. Extract any zip file
2. Run npm install && npm run dev
3. Explore the code
4. Try modifying things
5. See changes in real-time
---
## 🆘 Common Issues
| Problem | Solution |
|---------|----------|
| "npm: command not found" | Install Node.js from nodejs.org |
| "Port 3000 in use" | Use `npm run dev -- -p 3001` |
| Git tags not showing | You extracted individual zip, not complete |
| Changes not showing | Hard refresh browser: Ctrl+Shift+R |
| node_modules missing | Run `npm install` |
| TypeScript errors | All should build successfully |
---
## 📞 File Organization
All files are in one directory:
```
/demo-output/
├── (4 markdown files - documentation)
├── (10 zip files - project archives)
└── (this index file)
```
For best organization, create this structure:
```
QuickPoll-Demo/
├── Zips/
│ ├── stap-0.zip through stap-8.zip
│ ├── bonus.zip
│ └── quickpoll-demo-complete.zip
├── Documentation/
│ ├── QUICK-START.md
│ ├── TEACHER-GUIDE.md
│ ├── PROJECT-SUMMARY.md
│ ├── README.md
│ └── INDEX.md
└── Projects/
└── (extract zips here as needed)
```
---
## 🎉 You're Ready!
Everything is prepared for live-coding demonstrations. Start with QUICK-START.md and you'll have a successful classroom session.
Good luck teaching Next.js! 🚀
---
**Created**: March 17, 2026
**Files**: 15 total (4 docs + 11 zips)
**Total Size**: 588 KB
**Format**: Standard zip archives + Markdown
**Compatibility**: All platforms (Windows, Mac, Linux)

View File

@@ -1,264 +0,0 @@
# QuickPoll Next.js 15 Demo Project - Summary
## Project Description
QuickPoll is a complete polling application built with **Next.js 15**, **TypeScript**, and **Tailwind CSS**. Designed specifically for live-coding classroom demonstrations, it includes git tags at each development step.
## Technology Stack
- **Next.js 15** - React framework with App Router
- **TypeScript** - Fully typed JavaScript
- **Tailwind CSS** - Utility-first CSS framework
- **In-Memory Data** - Mock "database" for demo purposes
- **React Hooks** - useState for client-side state management
## Project Structure
```
quickpoll-demo/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── page.tsx # Homepage with poll list
│ │ ├── layout.tsx # Root layout with navbar
│ │ ├── globals.css # Global Tailwind styles
│ │ ├── loading.tsx # Loading skeleton
│ │ ├── error.tsx # Error boundary
│ │ ├── not-found.tsx # 404 page
│ │ ├── api/ # API route handlers
│ │ │ └── polls/
│ │ │ ├── route.ts # GET all, POST create
│ │ │ ├── [id]/
│ │ │ │ ├── route.ts # GET single poll
│ │ │ │ ├── vote/route.ts # POST vote
│ │ │ │ └── not-found.tsx
│ │ ├── poll/[id]/ # Dynamic poll detail page
│ │ │ └── page.tsx
│ │ └── create/ # Create new poll page
│ │ └── page.tsx
│ ├── components/ # Reusable components
│ │ └── VoteForm.tsx # Voting component
│ ├── lib/ # Utilities and data
│ │ └── data.ts # Mock poll data & functions
│ ├── types/ # TypeScript interfaces
│ │ └── index.ts # Poll, VoteBody, CreatePollBody
│ └── middleware.ts # Request logging middleware
├── .cursorrules # Code style guidelines
├── package.json
├── tsconfig.json
└── tailwind.config.ts
```
## Core Features
### 1. **Display Polls** (Homepage)
- Show all available polls as cards
- Display question, option count, total votes
- Click to navigate to poll detail page
### 2. **Poll Details** (Dynamic Page)
- Show full poll question and options
- Real-time voting results with percentage bars
- Server-rendered for SEO
### 3. **Vote on Polls** (VoteForm Component)
- Select an option
- Submit vote via POST API
- See results immediately after voting
- Purple theme with gradient progress bars
### 4. **Create Polls** (Bonus Step)
- Form to create new polls
- Input validation
- Dynamic option fields (add/remove)
- Automatic redirect to new poll
### 5. **API Routes**
- `GET /api/polls` - Get all polls
- `GET /api/polls/[id]` - Get single poll
- `POST /api/polls` - Create new poll
- `POST /api/polls/[id]/vote` - Vote on poll
### 6. **Middleware**
- Log all requests with timing
- Track API and poll page access
### 7. **Error Handling**
- Loading skeleton (Suspense)
- Error boundary component
- 404 pages (global and poll-specific)
## Git Tags for Progression
| Tag | Step | What's Added |
|-----|------|-------------|
| `stap-0` | Project Setup | Types, data, file structure, .cursorrules |
| `stap-1` | Layout & Navigation | Navbar, layout, metadata |
| `stap-2` | Homepage | Poll cards grid, Links |
| `stap-3` | API Routes | GET all, GET single, POST create |
| `stap-4` | Vote API & Demo | POST vote, Demo form at /demo |
| `stap-5` | Poll Detail Page | Dynamic [id] route, metadata |
| `stap-6` | VoteForm Component | Client component with voting logic |
| `stap-7` | Error Handling | loading.tsx, error.tsx, not-found.tsx |
| `stap-8` | Middleware | Request logging middleware |
| `bonus` | Create Poll | Full form, navbar link, remove demo |
## How to Use in Class
### Before Class
```bash
unzip quickpoll-demo-complete.zip
cd quickpoll-demo
npm install
npm run dev
```
### During Class
```bash
# Jump to any step
git checkout stap-3
# View what changed
git diff stap-2 stap-3
# Go to final version
git checkout bonus
```
### Demo the Voting API (stap-4)
1. Navigate to http://localhost:3000/demo
2. Show the form (projector)
3. Open DevTools Console to show logging
4. Open DevTools Network tab to show POST request
5. Display the fetch code example
## Teaching Points
### TypeScript & Types
- Define interfaces for data structures
- Export from `src/types/index.ts`
- Use types in function parameters
### Next.js Features
- App Router with file-based routing
- Dynamic routes: `[id]` creates `/poll/1`, `/poll/2`, etc.
- Server Components by default
- Client Components with "use client" directive
- API routes in `/app/api`
- Special files: `layout.tsx`, `not-found.tsx`, `error.tsx`, `loading.tsx`
### React Patterns
- Server Components (default, no interactivity)
- Client Components with useState
- Component composition (VoteForm in PollDetailPage)
- Fetch API for server communication
### Tailwind CSS
- Utility-first approach
- Responsive design (md:, lg: prefixes)
- Color system (purple-500, purple-600, purple-700)
- Hover states and transitions
### Form Handling
- Controlled inputs with useState
- Validation before submit
- Loading states during submission
- Error handling and feedback
## Sample Data
Three pre-loaded polls:
1. **"Wat is je favoriete programmeer taal?"**
- JavaScript, Python, TypeScript, Rust
- Sample votes: 45, 32, 28, 15
2. **"Hoe veel uur slaap krijg je per nacht?"**
- < 6 uur, 6-8 uur, 8+ uur
- Sample votes: 12, 68, 35
3. **"Welke framework gebruik je het meest?"**
- React, Vue, Svelte, Angular
- Sample votes: 89, 34, 12, 8
## Color Scheme
**Purple Theme for QuickPoll:**
- `#7c3aed` - purple-500 (main color)
- `#a855f7` - purple-600 (hover)
- `#9333ea` - purple-700 (pressed)
**Neutral Colors:**
- Gray-50 to Gray-900 for text and backgrounds
- White for cards and backgrounds
- Red-600 for error states
- Blue-50 for information
## Building & Deployment
### Build for Production
```bash
npm run build
npm start
```
### Build Verification
```bash
npm run build
```
Output shows:
- Static pages: `/`, `/create`
- Dynamic pages: `/poll/[id]`
- API routes: `/api/polls`, `/api/polls/[id]`, etc.
- Middleware (Proxy)
## Files Per Step
Each zip file contains the complete project at that step:
- All source code
- package.json with dependencies
- Configuration files
- **Excludes:** `node_modules/`, `.next/`, `.git/`
The `quickpoll-demo-complete.zip` includes:
- Full git history
- All commits and tags
- Ability to `git checkout` any step
## Typical Class Flow
1. **stap-0 to stap-2** - Show basic structure and homepage (15 min)
2. **stap-3** - Explain API routes (10 min)
3. **stap-4** - Demo the voting API in action (15 min) ⭐ Key demo point
4. **stap-5 to stap-6** - Build the voting interface (15 min)
5. **stap-7 to stap-8** - Error handling and middleware (10 min)
6. **bonus** - Create poll feature (optional, 10 min)
## Key Takeaways for Students
- Next.js makes building full-stack apps easy
- Server Components are the default (simpler, faster)
- API routes live alongside your pages
- TypeScript catches errors early
- Tailwind makes styling fast and consistent
- Git tags let you track feature development
## Common Questions
**Q: Is this production-ready?**
A: No - it uses in-memory data. For production, add a real database like PostgreSQL or MongoDB.
**Q: Can I add authentication?**
A: Yes - add NextAuth.js or similar. Future lesson!
**Q: How do I deploy this?**
A: Deploy to Vercel (created by Next.js team) or any Node.js hosting. See https://nextjs.org/docs/app/building-your-application/deploying
**Q: Can students modify the code?**
A: Absolutely! Use it as a starting point for assignments.
---
**Created:** March 2026
**Next.js Version:** 15.x
**Node Version:** 18+ recommended

View File

@@ -1,304 +0,0 @@
# QuickPoll Demo - Quick Start Guide
## For Tim - Before Your Class
### 1⃣ Prepare Your Setup (30 minutes before class)
```bash
# Extract the complete project
unzip quickpoll-demo-complete.zip
# Enter directory
cd quickpoll-demo
# Install dependencies
npm install
# Test that it runs
npm run dev
```
Should see:
```
- ready started server on 0.0.0.0:3000, url: http://localhost:3000
```
### 2⃣ Check All Git Tags Are Present
```bash
git tag -l
```
You should see:
```
bonus
stap-0
stap-1
stap-2
stap-3
stap-4
stap-5
stap-6
stap-7
stap-8
```
### 3⃣ Test Jumping Between Steps
```bash
# Go to step 2
git checkout stap-2
# Refresh browser - should show just poll cards
# Look at src/app/page.tsx - should be simple
# Go to final version
git checkout bonus
# Refresh browser - should show full app with "Nieuwe Poll" link
# Look at src/app/create/page.tsx - should exist
```
### 4⃣ Check Your Display Setup
- Open browser with http://localhost:3000
- Test font size on projector (should be easily readable)
- Test network tab in DevTools (for stap-4 demo)
- Test VS Code has good contrast
---
## During Your Class
### Starting the Session
```bash
# Make sure you're at the beginning
git checkout stap-0
npm run dev
```
Then open http://localhost:3000 in your browser.
### Jumping Between Steps
When you want to show a new step:
```bash
# Stop the dev server (Ctrl+C)
git checkout stap-3
# Restart dev server
npm run dev
# Browser should hot-reload automatically
# If not, refresh manually (Ctrl+R or Cmd+R)
```
### Key Demo Points
#### ⭐ Step 4 - The Voting API Demo (Most Important)
1. Navigate to http://localhost:3000/demo
2. Open DevTools (F12)
3. Go to Console tab - show logging
4. Go to Network tab
5. Fill the form with:
- Poll ID: `1`
- Option Index: `0`
6. Click "Stem" button
7. Watch the Network tab to show POST request
8. Show the JSON response
9. Show console logs
This is your most visual demonstration!
#### Step 1 - Layout
Show the navbar:
- Point out the "QuickPoll" branding (purple)
- Show the links being added
#### Step 2 - Homepage
Show poll cards appearing:
- Click one to navigate to detail page
- Show responsive grid
#### Step 3 - API Routes
Show file structure in VS Code:
- `src/app/api/polls/route.ts` - GET all, POST create
- `src/app/api/polls/[id]/route.ts` - GET single
- Point out `params: Promise<{ id: string }>` - Next.js 15 pattern
#### Step 5-6 - Poll Detail & Voting
Show the full flow:
1. Homepage → Click a poll
2. Vote on the poll
3. See results appear
4. Show purple gradient progress bars
#### Step 7-8 - Error Handling & Middleware
Show what happens when things break:
- Try navigating to non-existent poll (404 page)
- Point out loading skeleton briefly
- Show middleware logging in terminal
### Stopping the Dev Server
```bash
Ctrl+C
```
---
## Troubleshooting During Class
| Problem | Solution |
|---------|----------|
| Port 3000 in use | `npm run dev -- -p 3001` |
| Browser shows old code | Hard refresh: `Ctrl+Shift+R` or `Cmd+Shift+R` |
| Git checkout fails | Run `git status` to check for uncommitted changes, then `git stash` |
| DevTools won't show Network tab | Close and reopen DevTools (F12) |
| Terminal shows errors | Try: `npm install` then `npm run dev` again |
| Can't see projector clearly | Zoom in: `Ctrl/Cmd + +` in browser |
---
## File Locations You'll Reference
```
quickpoll-demo/
├── src/types/index.ts ← Show for TypeScript interfaces
├── src/lib/data.ts ← Show for sample data
├── src/app/layout.tsx ← Show for navbar
├── src/app/page.tsx ← Show for homepage
├── src/app/api/polls/ ← Show for API routes
├── src/app/poll/[id]/page.tsx ← Show for dynamic routing
├── src/components/VoteForm.tsx ← Show for client component
├── src/app/create/page.tsx ← Show for form handling
└── src/middleware.ts ← Show for middleware concept
```
---
## Live-Coding Tips
### Before You Type
- Take a screenshot for reference
- Know where you're going
- Have keyboard shortcuts ready
### While You Type
- Type **slowly** - let students follow
- **Talk through** what you're doing
- **Pause** at key lines to explain
- **Ask questions** - make it interactive
### Show Results
- Run frequently
- Show in browser on projector
- Use DevTools to demonstrate
- Let students ask questions
---
## Sample Dialog When Showing Step 4
> "Okay, we have our API route that accepts votes. But how does it work? Let me show you with this demo form."
>
> *Navigate to /demo*
>
> "Here we have a simple form. Let me open DevTools to show what happens behind the scenes."
>
> *Open DevTools, go to Console tab*
>
> "When I click 'Stem', two things happen:
> 1. The browser makes a request to our API
> 2. We get back the updated poll with new vote counts"
>
> *Click "Stem" button*
>
> "Look at the Console - you can see exactly what happened. Now let me show you in the Network tab..."
>
> *Go to Network tab*
>
> "Here's our POST request. The body shows we sent the option index (0), and the response shows the entire poll with updated vote counts."
---
## After Class
### Save Your Progress
The project uses git, so all your changes are tracked. If you made modifications, you can:
```bash
# See what you changed
git status
# Commit your changes
git add .
git commit -m "My live-coding edits"
```
### For Student Assignments
You can:
1. Share the `stap-5.zip` as a starting point
2. Have students add the VoteForm (stap-6 solution)
3. Or start with complete project and have them add features
### Deploy (Optional)
To deploy this app:
```bash
# Sign up at vercel.com
# Install Vercel CLI
npm i -g vercel
# Deploy
vercel
```
---
## Key Concepts to Emphasize
**Server Components** - Default in Next.js, simpler, faster
**Client Components** - Use "use client" only when needed
**API Routes** - Backend code in `/api` folder
**Dynamic Routes** - `[id]` creates flexible routing
**TypeScript** - Catch errors before they happen
**Tailwind CSS** - Write styles in HTML with utility classes
**Fetch API** - How frontend talks to backend
---
## Questions Students Will Ask
**"Why use Next.js instead of just React?"**
> Next.js gives you Server Components, API routes, better file structure, and automatic optimization. React is just the UI library - Next.js is the complete framework.
**"Can we use a database instead of in-memory data?"**
> Yes! Add PostgreSQL or MongoDB. That's another lesson though.
**"How long would this take to build from scratch?"**
> About 1-2 hours for a developer. We're doing it in steps so you can understand each concept.
**"Can we deploy this?"**
> Absolutely - to Vercel in 1 command. That's our bonus if we have time.
---
## You've Got This! 🎉
The project is ready. You have:
- ✅ Complete, working Next.js 15 app
- ✅ Git tags at every step
- ✅ Demo form for showing APIs
- ✅ Clear progression from basic to advanced
- ✅ Purple theme ready for classroom
- ✅ Console logs for debugging
Just follow this guide, and your students will love seeing a real app built step-by-step!

View File

@@ -1,278 +0,0 @@
# QuickPoll Next.js 15 Demo Project
A complete, step-by-step Next.js 15 polling application built for live-coding classroom demonstrations. Includes git tags at each development step for easy navigation during teaching.
## 📦 What's Included
### Zip Files (Individual Steps)
- `stap-0.zip` - Project setup + types + data
- `stap-1.zip` - Layout & Navigation
- `stap-2.zip` - Homepage with poll cards
- `stap-3.zip` - API routes (GET, POST)
- `stap-4.zip` - Vote API + demo form ⭐
- `stap-5.zip` - Poll detail page
- `stap-6.zip` - VoteForm component
- `stap-7.zip` - Error handling & loading states
- `stap-8.zip` - Middleware
- `bonus.zip` - Create poll page
### Complete Project
- `quickpoll-demo-complete.zip` - Full project with complete git history
### Documentation
- `QUICK-START.md` - Instructions for Tim (start here!)
- `TEACHER-GUIDE.md` - Comprehensive teaching guide
- `PROJECT-SUMMARY.md` - Technical overview
- `README.md` - This file
## 🚀 Getting Started
### Step 1: Extract & Setup
```bash
unzip quickpoll-demo-complete.zip
cd quickpoll-demo
npm install
npm run dev
```
### Step 2: Navigate to App
Open http://localhost:3000 in your browser
### Step 3: Jump Between Steps (During Teaching)
```bash
# View all available steps
git tag -l
# Jump to any step
git checkout stap-3
# See what changed in this step
git diff stap-2 stap-3
# Go to final version
git checkout bonus
```
## ✨ Key Features
- **9 Development Steps** - Logical progression from setup to full app
- **API Routes** - Backend routes in `/app/api`
- **Dynamic Routing** - `/poll/[id]` for individual poll pages
- **Real-Time Voting** - Vote and see results update instantly
- **Server Components** - Default Next.js 15 pattern
- **Client Components** - Interactive VoteForm with state
- **Error Handling** - Loading skeletons, error boundaries, 404 pages
- **TypeScript** - Fully typed throughout
- **Tailwind CSS** - Purple-themed responsive design
- **Middleware** - Request logging and timing
- **Demo Form** - Testing interface for API endpoints (stap-4)
## 📚 Documentation
### For Teachers
Start with **QUICK-START.md** - has everything you need before class
### For Understanding the Project
Read **PROJECT-SUMMARY.md** - complete technical overview
### For Teaching Details
See **TEACHER-GUIDE.md** - tips, key concepts, troubleshooting
## 🎯 Typical Class Flow
| Step | Duration | Focus |
|------|----------|-------|
| stap-0 to stap-2 | 15 min | File structure, homepage, cards |
| stap-3 | 10 min | API routes concept |
| stap-4 | 15 min | ⭐ Vote API demo (show DevTools!) |
| stap-5 to stap-6 | 15 min | Dynamic pages, voting interface |
| stap-7 to stap-8 | 10 min | Error handling, middleware |
| bonus | 10 min | Form handling, creating polls |
**Total: ~75 minutes for complete lesson**
## 💡 Key Teaching Moments
### stap-4: The Demo Form
This is your star moment! Show:
1. Demo form at http://localhost:3000/demo
2. Open DevTools Console - show logging
3. Switch to Network tab - show POST request
4. Click "Stem" and watch the request happen
5. Show the JSON response with updated votes
### stap-6: VoteForm Component
Explain:
- Why "use client" is needed (state management)
- How useState tracks selected option
- How fetch() sends vote to API
- How component updates when response comes back
### All Steps
Keep showing the running app - live feedback is key!
## 📋 What's in Each Step
### stap-0: Project Setup
- Create Next.js with TypeScript + Tailwind
- Define Poll interfaces
- Create mock data (3 sample polls)
- Set up folder structure
### stap-1: Layout & Navigation
- Add navbar with branding
- Set up global layout
- Add metadata
### stap-2: Homepage
- Display all polls as cards
- Show poll stats (option count, vote count)
- Add links to individual polls
### stap-3: API Routes
- `GET /api/polls` - fetch all polls
- `GET /api/polls/[id]` - fetch single poll
- `POST /api/polls` - create new poll
### stap-4: Vote API + Demo
- `POST /api/polls/[id]/vote` - record a vote
- Demo form at `/demo` for testing
### stap-5: Poll Detail Page
- Dynamic route `/poll/[id]`
- Dynamic metadata for SEO
- Show poll questions and options
### stap-6: VoteForm Component
- Client component with voting logic
- Real-time results with percentage bars
- Purple gradient progress bars
### stap-7: Error Handling
- Loading skeleton (Suspense)
- Error boundary component
- 404 pages
### stap-8: Middleware
- Request logging
- Timing middleware
- Route matching
### bonus: Create Poll
- Form to create new polls
- Dynamic option inputs
- Form validation
- Navigation after creation
## 🛠 Technology Stack
- **Next.js 15** - React framework
- **TypeScript** - Type-safe JavaScript
- **Tailwind CSS** - Utility-first CSS
- **React Hooks** - useState for interactivity
- **Fetch API** - For API calls
- **Git** - Version control with tags
## 🎨 Design System
**Purple Theme:**
- Primary: `#7c3aed` (purple-500)
- Hover: `#a855f7` (purple-600)
- Active: `#9333ea` (purple-700)
**Neutral:**
- Light backgrounds: Gray-50
- Text: Gray-900
- Borders: Gray-200
- Errors: Red-600
## ✅ Build & Deployment
### Verify Build
```bash
npm run build
```
### Deploy to Vercel
```bash
npm i -g vercel
vercel
```
One command deployment! (You need a Vercel account)
## 📌 Important Notes
### In-Memory Data
This project uses in-memory JavaScript arrays for data. In production, you'd use:
- PostgreSQL
- MongoDB
- Supabase
- Firebase
### No Authentication
This project doesn't have user accounts. Real apps would have:
- User authentication
- User-specific voting history
- Admin panels
### No Database
Each time you restart the app, votes reset. That's fine for demos!
## 🤔 Common Questions
**Q: Is this production-ready?**
A: No - it's designed for learning. Add a database for production.
**Q: Can I modify it for my class?**
A: Yes! Use it as a starting point for assignments.
**Q: How do I extend it?**
A: Add a real database (PostgreSQL with Prisma is popular), add authentication, add user accounts, etc.
**Q: Where can students learn more?**
A: https://nextjs.org/learn - official Next.js tutorial
## 📞 Support
Each zip file is independent and self-contained. You can:
1. Extract any `stap-X.zip` individually
2. Run `npm install` and `npm run dev`
3. See the app at that stage of development
The `quickpoll-demo-complete.zip` has git history, so you can:
1. `git log --oneline` - see all commits
2. `git checkout stap-3` - jump to any step
3. `git diff stap-2 stap-3` - see what changed
## 🎓 For Students
After the lesson, students can:
1. Clone the repo and explore the code
2. Modify styling with Tailwind
3. Add new features (poll deletion, editing, etc.)
4. Connect to a real database
5. Deploy to Vercel
This is a great foundation for learning full-stack web development!
## 📝 Notes
- All code in English (variable names, comments)
- UI text in Dutch (button labels, messages)
- Fully typed TypeScript throughout
- Comprehensive comments for teaching
- Clean, readable code style
## 🎉 Ready to Teach!
You have everything you need. Good luck with your live-coding demonstration!
Start with **QUICK-START.md** for immediate instructions.
---
**Version:** 1.0
**Next.js:** 15.x
**Node:** 18+ recommended
**Date Created:** March 2026

View File

@@ -1,311 +0,0 @@
# QuickPoll Next.js Demo - Live Coding Guide for Tim
## Overview
This is a complete, step-by-step Next.js 15 project built for live-coding demonstrations. Each step has a git tag, allowing you to jump between different stages of the application during class.
## File Structure
```
demo-output/
├── stap-0.zip # Project setup + types + data
├── stap-1.zip # Layout & Navigation
├── stap-2.zip # Homepage with poll cards
├── stap-3.zip # API routes (GET all, GET single, POST create)
├── stap-4.zip # POST vote route + demo form
├── stap-5.zip # Poll detail page
├── stap-6.zip # VoteForm component
├── stap-7.zip # Loading, error, not-found pages
├── stap-8.zip # Middleware
├── bonus.zip # Create poll page
└── quickpoll-demo-complete.zip # Full project with complete git history
```
## How to Use in Live-Coding Class
### 1. **Setup Before Class**
Extract `quickpoll-demo-complete.zip`:
```bash
unzip quickpoll-demo-complete.zip
cd quickpoll-demo
npm install
```
### 2. **Jump Between Steps During Class**
Use git checkout to jump to any step:
```bash
# Go to beginning (step 0)
git checkout stap-0
# Progress to step 1
git checkout stap-1
# Jump to step 4 to show the demo form
git checkout stap-4
# Go back to the final version
git checkout bonus
```
### 3. **Run the Application**
```bash
npm run dev
```
Then open `http://localhost:3000` in your browser and use the projector to show students.
### 4. **Show the Demo Form (stap-4)**
When you checkout `stap-4`, a demo page is available at `http://localhost:3000/demo`:
- Show students how to use the form to test the vote API
- Open DevTools (F12) → Network tab to show the POST request
- Open Console to show the console.log messages
- Display the fetch code example to teach how API calls work
## What Each Step Covers
### stap-0: Project Setup
- Create Next.js project with TypeScript and Tailwind
- Set up types and data structures
- Initial folder structure with in-memory "database"
- Includes `.cursorrules` for consistent code style
**Files to show:**
- `src/types/index.ts` - Poll and VoteBody interfaces
- `src/lib/data.ts` - Sample polls data and functions
### stap-1: Layout & Navigation
- Add navbar with QuickPoll branding
- Add navigation links (Home, Nieuwe Poll in bonus)
- Update metadata and HTML structure
- Global Tailwind styling
**Files to show:**
- `src/app/layout.tsx` - Layout component with navbar
### stap-2: Homepage
- Display all polls as cards
- Show poll question, number of options, total votes
- Add hover effects and links to poll detail pages
**Files to show:**
- `src/app/page.tsx` - Homepage with poll cards grid
- Tailwind classes for responsive design
### stap-3: API Routes
- Create `GET /api/polls` - fetch all polls
- Create `GET /api/polls/[id]` - fetch single poll
- Create `POST /api/polls` - create new poll
- Use Next.js 15 async params pattern
**Files to show:**
- `src/app/api/polls/route.ts`
- `src/app/api/polls/[id]/route.ts`
- Point out `params: Promise<{ id: string }>` pattern
### stap-4: Vote API Route + Demo Form
- Create `POST /api/polls/[id]/vote` - vote on a poll
- Input validation and error handling
- **Demo form at `/demo`** - interactive testing page
- Console.log statements for teaching
- Display fetch code example
**Files to show:**
- `src/app/api/polls/[id]/vote/route.ts`
- `src/app/demo/page.tsx` - the demo form (use projector!)
- DevTools Console to show logging
- DevTools Network tab to show POST requests
### stap-5: Poll Detail Page
- Create `src/app/poll/[id]/page.tsx` (Server Component)
- Add `generateMetadata()` for dynamic SEO
- Use `notFound()` for missing polls
- Placeholder for VoteForm (comes next)
**Files to show:**
- `src/app/poll/[id]/page.tsx`
- Point out that this is a Server Component (no "use client")
### stap-6: VoteForm Component
- Create `src/components/VoteForm.tsx` ("use client")
- Implement voting with state management
- Show results after voting with percentage bars
- Purple theme (#7c3aed) with gradient progress bars
- Real-time vote count updates
**Files to show:**
- `src/components/VoteForm.tsx`
- Explain useState and fetch inside a component
- Show console.log for vote submissions
- Update to `src/app/poll/[id]/page.tsx` to use VoteForm
### stap-7: Error Handling Pages
- Create `src/app/loading.tsx` - skeleton loader
- Create `src/app/error.tsx` - error boundary
- Create `src/app/not-found.tsx` - global 404 page
- Create `src/app/poll/[id]/not-found.tsx` - poll-specific 404
**Files to show:**
- Explain Suspense boundaries and error handling
- Show how Next.js automatically uses these files
### stap-8: Middleware
- Create `src/middleware.ts` - request logging
- Log request timing and paths
- Use matcher for selective routes
**Files to show:**
- `src/middleware.ts`
- Point out the matcher configuration
### bonus: Create Poll Page
- Create `src/app/create/page.tsx` - full form with validation
- Dynamic option inputs (add/remove)
- POST to `/api/polls` and redirect
- Remove demo page (was temporary for stap-4)
- Add "Nieuwe Poll" link to navbar
**Files to show:**
- `src/app/create/page.tsx` - form implementation
- Error handling and validation
## Teaching Tips
### Code Highlighting
- Use VS Code theme with good contrast on projector
- Increase font size (at least 16-18pt)
- Use "Zen Mode" or "Fullscreen" for less distraction
### Live Coding Strategy
1. **Type slowly** - Let students follow along
2. **Explain as you type** - Narrate what you're doing
3. **Run often** - Show working results frequently
4. **Use DevTools** - Show Network and Console tabs
5. **Ask questions** - Make it interactive
### DevTools Demonstrations
**When showing stap-4 demo:**
1. Open http://localhost:3000/demo
2. Open DevTools (F12)
3. Go to Console tab - show console.log messages
4. Go to Network tab
5. Fill the form and click "Stem"
6. Show the POST request in Network tab
7. Show the JSON response
8. Check the Console for detailed logging
### Key Concepts to Teach
- **Server Components vs Client Components** - explain when to use "use client"
- **Dynamic Routes** - show how `[id]` creates dynamic pages
- **API Routes** - show how `/api` routes handle requests
- **Middleware** - show how to run code before requests
- **Tailwind CSS** - explain utility-first CSS
- **State Management** - useState in VoteForm
- **Fetch API** - how components communicate with API
### Projector Tips
- Test the layout on a real projector before class
- Use a light theme for better visibility
- Zoom in on code snippets (Ctrl/Cmd + in browser)
- Keep demos simple - don't try to build everything from scratch
## File Sizes
- `stap-0.zip` to `stap-8.zip`: ~32-43 KB each
- `bonus.zip`: ~44 KB
- `quickpoll-demo-complete.zip`: ~160 KB (includes full git history)
## Git Commands for Students
After you show them the steps, students can explore:
```bash
# See all commits
git log --oneline
# See all tags
git tag -l
# Jump to a specific step
git checkout stap-3
# See what changed in this step
git diff stap-2 stap-3
# Go back to latest
git checkout bonus
```
## Troubleshooting
### Port 3000 already in use
```bash
npm run dev -- -p 3001
```
### Changes not showing
```bash
# Hard refresh in browser
Ctrl+Shift+R (Windows/Linux)
Cmd+Shift+R (Mac)
```
### Git checkout doesn't work
```bash
# Make sure you're in the project directory
cd quickpoll-demo
# See current status
git status
# If you have uncommitted changes
git stash
# Then try checkout again
git checkout stap-5
```
### Build errors
```bash
# Clear Next.js cache
rm -rf .next
# Reinstall dependencies
npm install
# Try building
npm run build
```
## Project Structure Notes
- **src/app/** - Next.js App Router pages and layouts
- **src/components/** - Reusable React components
- **src/lib/** - Utility functions and mock data
- **src/types/** - TypeScript interfaces
- **src/middleware.ts** - Request middleware
- **public/** - Static assets
## Additional Resources
- Next.js 15 docs: https://nextjs.org/docs
- Tailwind CSS: https://tailwindcss.com/docs
- React: https://react.dev
## Next Steps After the Demo
Students can:
1. Modify poll questions and options
2. Add new features (e.g., poll deletion, editing)
3. Connect to a real database (PostgreSQL, MongoDB, etc.)
4. Add authentication
5. Deploy to Vercel
Good luck with your live-coding demonstration! 🎉

View File

@@ -1,6 +0,0 @@
You are a Next.js 15 expert using App Router with TypeScript.
Use server components by default.
Use "use client" only when needed for interactivity.
Always define TypeScript interfaces for props, params, and API bodies.
Use Tailwind CSS for styling.
Use the @/ import alias for all local imports.

View File

@@ -1,41 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@@ -1,36 +0,0 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

View File

@@ -1,7 +0,0 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +0,0 @@
{
"name": "quickpoll",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"next": "16.1.6",
"react": "19.2.3",
"react-dom": "19.2.3"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"tailwindcss": "^4",
"typescript": "^5"
}
}

View File

@@ -1,7 +0,0 @@
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;

View File

@@ -1 +0,0 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 391 B

View File

@@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 128 B

View File

@@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

Before

Width:  |  Height:  |  Size: 385 B

View File

@@ -1,27 +0,0 @@
import { NextResponse } from "next/server";
import { getPollById } from "@/lib/data";
import type { Poll } from "@/types";
interface RouteParams {
params: Promise<{ id: string }>;
}
// STAP 3: GET /api/polls/[id] — enkele poll ophalen
// (Dit bestand is COMPLEET van Les 5. Dit is stap 3 van Les 5.)
export async function GET(
request: Request,
{ params }: RouteParams
): Promise<NextResponse> {
const { id } = await params;
const poll = getPollById(id);
if (!poll) {
return NextResponse.json(
{ error: "Poll niet gevonden" },
{ status: 404 }
);
}
return NextResponse.json<Poll>(poll);
}

View File

@@ -1,28 +0,0 @@
import { NextResponse } from "next/server";
import { votePoll } from "@/lib/data";
interface RouteParams {
params: Promise<{ id: string }>;
}
interface VoteBody {
optionIndex: number;
}
// STAP 4: POST /api/polls/[id]/vote — stem uitbrengen
//
// Wat moet je doen?
// 1. Haal het id op uit params (await!)
// 2. Lees de request body en cast naar VoteBody
// 3. Valideer: is optionIndex een number?
// 4. Roep votePoll(id, body.optionIndex) aan
// 5. Als het resultaat undefined is: return 404
// 6. Anders: return de geüpdatete poll als JSON
export async function POST(
request: Request,
{ params }: RouteParams
): Promise<NextResponse> {
// Implementeer je POST handler hier
return NextResponse.json({ error: "Nog niet geimplementeerd" }, { status: 501 });
}

View File

@@ -1,24 +0,0 @@
import { NextResponse } from "next/server";
import { getPolls, createPoll } from "@/lib/data";
import type { Poll, CreatePollBody } from "@/types";
// GET /api/polls — alle polls ophalen
export async function GET(): Promise<NextResponse<Poll[]>> {
const polls = getPolls();
return NextResponse.json(polls);
}
// POST /api/polls — nieuwe poll aanmaken
export async function POST(request: Request): Promise<NextResponse> {
const body: CreatePollBody = await request.json();
if (!body.question || !body.options || body.options.length < 2) {
return NextResponse.json(
{ error: "Vraag en minstens 2 opties zijn verplicht" },
{ status: 400 }
);
}
const newPoll = createPoll(body.question, body.options);
return NextResponse.json(newPoll, { status: 201 });
}

View File

@@ -1,32 +0,0 @@
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
// BONUS: Maak een formulier om een nieuwe poll aan te maken
//
// Benodigde state:
// - question: string
// - options: string[] (start met ["", ""])
// - isSubmitting: boolean
// - error: string | null
//
// Wat moet je bouwen?
// 1. Een input voor de vraag
// 2. Inputs voor de opties (minimaal 2, maximaal 6)
// 3. Knoppen om opties toe te voegen/verwijderen
// 4. Een submit knop die POST naar /api/polls
// 5. Na success: redirect naar / met router.push("/")
export default function CreatePollPage() {
return (
<div className="max-w-2xl mx-auto">
<h1 className="text-2xl font-bold text-gray-900 mb-6">
Nieuwe Poll Aanmaken
</h1>
<p className="text-gray-400 italic">
Bonus: bouw hier het create formulier
</p>
</div>
);
}

View File

@@ -1,30 +0,0 @@
"use client";
// STAP 7: Error boundary
//
// Dit bestand vangt fouten op in de route.
// MOET een client component zijn ("use client" staat al bovenaan).
//
// Props die je krijgt:
// - error: Error — het error object met .message
// - reset: () => void — functie om de pagina opnieuw te proberen
//
// Bouw een nette error pagina met:
// - Een titel "Er ging iets mis!"
// - De error message
// - Een "Probeer opnieuw" knop die reset() aanroept
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
return (
<div className="text-center py-16">
<p>Er ging iets mis: {error.message}</p>
<button onClick={() => reset()}>Probeer opnieuw</button>
</div>
);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -1 +0,0 @@
@import "tailwindcss";

View File

@@ -1,36 +0,0 @@
import type { Metadata } from "next";
import Link from "next/link";
import "./globals.css";
export const metadata: Metadata = {
title: "QuickPoll — Stem op alles",
description: "Maak en deel polls met je vrienden",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="nl">
<body className="min-h-screen bg-gray-50 text-gray-900">
{/*
STAP 1: Bouw hier een navigatiebalk met:
- Logo/titel "QuickPoll" (links) die linkt naar /
- Een link naar / ("Polls")
- Een link naar /create ("Nieuwe Poll")
Tip: gebruik <Link> van "next/link", niet <a>
Tip: gebruik Tailwind classes voor styling
*/}
<main className="max-w-4xl mx-auto px-4 py-8">{children}</main>
<footer className="text-center text-gray-400 text-sm py-8">
© 2025 QuickPoll NOVI Hogeschool Les 5
</footer>
</body>
</html>
);
}

View File

@@ -1,17 +0,0 @@
// STAP 7: Loading state
//
// Dit bestand wordt automatisch getoond terwijl een pagina laadt.
// Bouw een skeleton loader met Tailwind's animate-pulse class.
//
// Voorbeeld:
// <div className="animate-pulse">
// <div className="h-8 bg-gray-200 rounded w-1/3 mb-4" />
// </div>
export default function Loading() {
return (
<div>
<p>Laden...</p>
</div>
);
}

View File

@@ -1,18 +0,0 @@
import Link from "next/link";
// STAP 7: Not found pagina
//
// Wordt getoond als een pagina niet bestaat.
// Bouw een nette 404 pagina met een link terug naar home.
export default function NotFound() {
return (
<div className="text-center py-16">
<h2 className="text-4xl font-bold text-gray-900 mb-4">404</h2>
<p className="text-gray-600 mb-6">Deze pagina bestaat niet.</p>
<Link href="/" className="text-purple-600 hover:underline">
Terug naar home
</Link>
</div>
);
}

View File

@@ -1,31 +0,0 @@
import Link from "next/link";
import { getPolls } from "@/lib/data";
import type { Poll } from "@/types";
export const dynamic = "force-dynamic";
export default function HomePage() {
// STAP 2: Haal alle polls op met getPolls()
// Dit is een Server Component — je kunt gewoon functies aanroepen!
return (
<div>
<h1 className="text-3xl font-bold text-gray-900 mb-2">Actieve Polls</h1>
<p className="text-gray-500 mb-8">Klik op een poll om te stemmen</p>
<div className="grid gap-4">
{/*
STAP 2: Map over de polls en toon voor elke poll:
- De vraag (poll.question)
- Het aantal opties en stemmen
- De opties als tags/badges
- Wrap het in een <Link> naar /poll/{poll.id}
Tip: maak een helper functie voor het totaal aantal stemmen:
const totalVotes = (poll: Poll): number =>
poll.votes.reduce((sum, v) => sum + v, 0);
*/}
</div>
</div>
);
}

View File

@@ -1,28 +0,0 @@
"use client";
// STAP 7: Error boundary voor poll detail pagina
//
// Dit bestand vangt fouten op in deze route.
export default function ErrorPoll({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div className="max-w-2xl mx-auto text-center py-12">
<h2 className="text-2xl font-bold text-red-600 mb-4">
Oeps! Iets ging fout
</h2>
<p className="text-gray-600 mb-6">{error.message}</p>
<button
onClick={reset}
className="bg-red-600 text-white px-4 py-2 rounded-lg hover:bg-red-700"
>
Probeer opnieuw
</button>
</div>
);
}

View File

@@ -1,20 +0,0 @@
// STAP 7: Loading state voor poll detail pagina
//
// Dit bestand wordt automatisch getoond terwijl de poll detail pagina laadt.
// Bouw een skeleton loader die lijkt op de echte content.
export default function LoadingPoll() {
return (
<div className="max-w-2xl mx-auto animate-pulse">
<div className="h-10 bg-gray-200 rounded w-3/4 mb-8" />
<div className="space-y-4">
{[1, 2, 3].map((i) => (
<div
key={i}
className="bg-white rounded-lg border p-4 h-16"
/>
))}
</div>
</div>
);
}

View File

@@ -1,17 +0,0 @@
import Link from "next/link";
export default function PollNotFound() {
return (
<div className="text-center py-16">
<h2 className="text-2xl font-bold text-gray-900 mb-4">
Poll niet gevonden
</h2>
<p className="text-gray-600 mb-6">
Deze poll bestaat niet of is verwijderd.
</p>
<Link href="/" className="text-purple-600 hover:underline">
Bekijk alle polls
</Link>
</div>
);
}

View File

@@ -1,40 +0,0 @@
import { notFound } from "next/navigation";
import { getPollById } from "@/lib/data";
import { VoteForm } from "@/components/VoteForm";
import type { Metadata } from "next";
interface PageProps {
params: Promise<{ id: string }>;
}
// STAP 5: generateMetadata — dynamische pagina titel
//
// Deze functie genereert de <title> tag voor SEO.
// Haal de poll op en return de vraag als titel.
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { id } = await params;
const poll = getPollById(id);
return {
title: poll ? `${poll.question} — QuickPoll` : "Poll niet gevonden",
};
}
// STAP 5: PollPage — de poll detail pagina
//
// Wat moet je doen?
// 1. Haal het id op uit params (await!)
// 2. Zoek de poll met getPollById(id)
// 3. Als de poll niet bestaat: roep notFound() aan
// 4. Render de poll vraag als <h1>
// 5. Render de <VoteForm poll={poll} /> component
export default async function PollPage({ params }: PageProps) {
// Implementeer je pagina hier
return (
<div className="max-w-2xl mx-auto">
<p>Implementeer deze pagina (zie stap 5 in de opdracht)</p>
</div>
);
}

View File

@@ -1,129 +0,0 @@
"use client";
import { useState } from "react";
import Link from "next/link";
import type { Poll } from "@/types";
interface VoteFormProps {
poll: Poll;
}
// STAP 6: VoteForm — de stem interface
//
// Dit is een CLIENT component ("use client" staat bovenaan).
// Hier mag je wel useState en onClick gebruiken!
//
// Benodigde state:
// - selectedOption: number | null (welke optie is geselecteerd)
// - hasVoted: boolean (heeft de gebruiker al gestemd)
// - isLoading: boolean (wordt het formulier verstuurd)
// - currentPoll: Poll (de huidige poll data, update na stemmen)
//
// Wat moet je bouwen?
// 1. Toon alle opties als klikbare knoppen (voor het stemmen)
// 2. Highlight de geselecteerde optie met purple border
// 3. Een "Stem!" knop die een POST doet naar /api/polls/{id}/vote
// 4. Na het stemmen: toon de resultaten met progress bars en percentages
// 5. Toon een "Terug" link naar de homepage
export function VoteForm({ poll }: VoteFormProps) {
const [selectedOption, setSelectedOption] = useState<number | null>(null);
const [hasVoted, setHasVoted] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [currentPoll, setCurrentPoll] = useState<Poll>(poll);
const totalVotes: number = currentPoll.votes.reduce((sum, v) => sum + v, 0);
function getPercentage(votes: number): number {
if (totalVotes === 0) return 0;
return Math.round((votes / totalVotes) * 100);
}
async function handleVote(): Promise<void> {
if (selectedOption === null || isLoading) return;
setIsLoading(true);
try {
const response = await fetch(`/api/polls/${currentPoll.id}/vote`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ optionIndex: selectedOption }),
});
if (response.ok) {
const updatedPoll: Poll = await response.json();
setCurrentPoll(updatedPoll);
setHasVoted(true);
}
} finally {
setIsLoading(false);
}
}
// Nog niet gestemd — toon opties
if (!hasVoted) {
return (
<div className="space-y-4">
{currentPoll.options.map((option, idx) => (
<button
key={idx}
onClick={() => setSelectedOption(idx)}
disabled={isLoading}
className={`w-full text-left p-4 rounded-lg border-2 transition ${
selectedOption === idx
? "border-purple-500 bg-purple-50"
: "border-gray-200 hover:border-purple-300"
}`}
>
<span className="font-medium">{option}</span>
</button>
))}
<button
onClick={handleVote}
disabled={selectedOption === null || isLoading}
className="w-full bg-purple-600 text-white py-3 rounded-lg hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed transition font-medium"
>
{isLoading ? "Aan het stemmen..." : "Stem!"}
</button>
</div>
);
}
// Wel gestemd — toon resultaten
return (
<div className="space-y-6">
<p className="text-green-600 font-medium text-lg">
Bedankt voor je stem!
</p>
<div className="space-y-4">
{currentPoll.options.map((option, idx) => (
<div key={idx}>
<div className="flex justify-between mb-2">
<span className="font-medium">{option}</span>
<span className="text-sm text-gray-600">
{currentPoll.votes[idx]} stemmen ({getPercentage(currentPoll.votes[idx])}%)
</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-3">
<div
className="bg-purple-600 h-3 rounded-full transition-all"
style={{
width: `${getPercentage(currentPoll.votes[idx])}%`,
}}
/>
</div>
</div>
))}
</div>
<Link
href="/"
className="block text-center text-purple-600 hover:underline mt-6"
>
Terug naar polls
</Link>
</div>
);
}

View File

@@ -1,55 +0,0 @@
import { Poll } from "@/types";
export const polls: Poll[] = [
{
id: "1",
question: "Wat is de beste code editor?",
options: ["VS Code", "Cursor", "Vim", "WebStorm"],
votes: [12, 25, 5, 3],
},
{
id: "2",
question: "Wat is de beste programmeertaal?",
options: ["TypeScript", "Python", "Rust", "Go"],
votes: [18, 15, 8, 4],
},
{
id: "3",
question: "Welk framework heeft de toekomst?",
options: ["Next.js", "Remix", "Astro", "SvelteKit"],
votes: [22, 6, 10, 7],
},
];
let nextId = 4;
export function getPolls(): Poll[] {
return polls;
}
export function getPollById(id: string): Poll | undefined {
return polls.find((poll) => poll.id === id);
}
export function createPoll(question: string, options: string[]): Poll {
const newPoll: Poll = {
id: String(nextId++),
question,
options,
votes: new Array(options.length).fill(0),
};
polls.push(newPoll);
return newPoll;
}
export function votePoll(
pollId: string,
optionIndex: number
): Poll | undefined {
const poll = polls.find((p) => p.id === pollId);
if (!poll || optionIndex < 0 || optionIndex >= poll.options.length) {
return undefined;
}
poll.votes[optionIndex]++;
return poll;
}

View File

@@ -1,17 +0,0 @@
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest): NextResponse {
const start = Date.now();
console.log(`[${request.method}] ${request.nextUrl.pathname}`);
const response = NextResponse.next();
response.headers.set("x-request-time", String(Date.now() - start));
return response;
}
export const config = {
matcher: ["/api/:path*", "/poll/:path*"],
};

View File

@@ -1,11 +0,0 @@
export interface Poll {
id: string;
question: string;
options: string[];
votes: number[];
}
export interface CreatePollBody {
question: string;
options: string[];
}

View File

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