fix: les 6
This commit is contained in:
@@ -1,444 +1,330 @@
|
||||
# Les 14: Project Setup & Repository Structure
|
||||
# Les 14: AI Chat Interface & Streaming
|
||||
|
||||
---
|
||||
|
||||
## Hoofdstuk
|
||||
**Deel 4: Advanced AI Features** (Les 13-18)
|
||||
**Deel 4: Advanced AI & Deployment** (Les 13-18)
|
||||
|
||||
## Beschrijving
|
||||
Leer professionele project setup en repository structuur. Begrijp hoe een goed georganiseerd project AI tools effectiever maakt en samenwerking vergemakkelijkt.
|
||||
Bouwen van professionele chat interfaces met streaming responses. Message rendering, markdown support, error handling, loading states, en UX patterns voor AI-powered features.
|
||||
|
||||
---
|
||||
|
||||
## Te Behandelen
|
||||
## Te Behandelen (~45 min)
|
||||
|
||||
### Groepsdiscussie (15 min)
|
||||
Bespreek klassikaal de AI assistant reflecties uit Les 13 - welke instructies werkten goed en welke niet?
|
||||
|
||||
### Waarom Project Structuur Belangrijk Is
|
||||
|
||||
**Voor jezelf:**
|
||||
- Sneller code terugvinden
|
||||
- Makkelijker onderhouden
|
||||
- Minder bugs door consistentie
|
||||
|
||||
**Voor AI tools:**
|
||||
- Betere context understanding
|
||||
- Consistentere code generation
|
||||
- Cursor/Claude begrijpt je project beter
|
||||
|
||||
**Voor samenwerking:**
|
||||
- Anderen begrijpen je code sneller
|
||||
- Standaard conventies = minder discussie
|
||||
- Onboarding nieuwe developers eenvoudiger
|
||||
- Chat UI patterns en best practices
|
||||
- useChat hook deep dive (state, loading, error)
|
||||
- Streaming response handling en real-time updates
|
||||
- Message rendering strategies en optimizations
|
||||
- Markdown rendering in chat messages
|
||||
- Error handling en error boundaries
|
||||
- Loading states en skeleton loaders
|
||||
- User input validation and sanitization
|
||||
- Accessibility in chat interfaces (ARIA labels)
|
||||
- Message persistence (localStorage of database)
|
||||
- Performance optimization
|
||||
|
||||
---
|
||||
|
||||
### Next.js 14 Project Structuur
|
||||
### useChat Hook Deep Dive
|
||||
|
||||
**Aanbevolen structuur:**
|
||||
```
|
||||
project-root/
|
||||
├── src/
|
||||
│ ├── app/ # Next.js App Router
|
||||
│ │ ├── (auth)/ # Route group voor auth pagina's
|
||||
│ │ │ ├── login/
|
||||
│ │ │ └── register/
|
||||
│ │ ├── api/ # API routes
|
||||
│ │ │ └── chat/
|
||||
│ │ ├── dashboard/
|
||||
│ │ ├── layout.tsx
|
||||
│ │ ├── page.tsx
|
||||
│ │ └── globals.css
|
||||
│ │
|
||||
│ ├── components/ # React components
|
||||
│ │ ├── ui/ # Basis UI components
|
||||
│ │ │ ├── Button.tsx
|
||||
│ │ │ ├── Input.tsx
|
||||
│ │ │ └── Card.tsx
|
||||
│ │ ├── layout/ # Layout components
|
||||
│ │ │ ├── Header.tsx
|
||||
│ │ │ ├── Footer.tsx
|
||||
│ │ │ └── Sidebar.tsx
|
||||
│ │ └── features/ # Feature-specifieke components
|
||||
│ │ ├── auth/
|
||||
│ │ └── dashboard/
|
||||
│ │
|
||||
│ ├── lib/ # Utilities en configuraties
|
||||
│ │ ├── supabase.ts # Supabase client
|
||||
│ │ ├── utils.ts # Helper functies
|
||||
│ │ └── constants.ts # App constanten
|
||||
│ │
|
||||
│ ├── hooks/ # Custom React hooks
|
||||
│ │ ├── useAuth.ts
|
||||
│ │ └── useTodos.ts
|
||||
│ │
|
||||
│ └── types/ # TypeScript types
|
||||
│ ├── database.ts
|
||||
│ └── api.ts
|
||||
│
|
||||
├── public/ # Static assets
|
||||
│ ├── images/
|
||||
│ └── favicon.ico
|
||||
│
|
||||
├── docs/ # Documentatie
|
||||
│ ├── PROMPT-LOG.md
|
||||
│ ├── AI-DECISIONS.md
|
||||
│ └── PROJECT-BRIEF.md
|
||||
│
|
||||
├── .cursorrules # Cursor AI configuratie
|
||||
├── .env.local # Environment variables (niet in git!)
|
||||
├── .env.example # Template voor env vars
|
||||
├── .gitignore
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
├── tailwind.config.ts
|
||||
└── README.md
|
||||
**State management met useChat:**
|
||||
|
||||
```typescript
|
||||
'use client'
|
||||
import { useChat } from 'ai/react'
|
||||
|
||||
export function ChatComponent() {
|
||||
const {
|
||||
messages, // All messages in conversation
|
||||
input, // Current input text
|
||||
handleInputChange, // Update input
|
||||
handleSubmit, // Send message
|
||||
isLoading, // Is AI responding?
|
||||
error, // Any errors?
|
||||
} = useChat({
|
||||
api: '/api/chat',
|
||||
initialMessages: [], // Optional: pre-load messages
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{messages.map((msg) => (
|
||||
<div key={msg.id}>
|
||||
<strong>{msg.role}:</strong> {msg.content}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{isLoading && <div>AI is thinking...</div>}
|
||||
{error && <div>Error: {error.message}</div>}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input
|
||||
value={input}
|
||||
onChange={handleInputChange}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<button type="submit" disabled={isLoading}>Send</button>
|
||||
</form>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Component Organisatie
|
||||
### Message Rendering Patterns
|
||||
|
||||
**UI Components (src/components/ui/):**
|
||||
- Herbruikbare, generieke components
|
||||
- Geen business logic
|
||||
- Props-driven
|
||||
- Voorbeelden: Button, Input, Modal, Card
|
||||
|
||||
**Layout Components (src/components/layout/):**
|
||||
- Structurele components
|
||||
- Meestal één per type
|
||||
- Voorbeelden: Header, Footer, Sidebar, Navigation
|
||||
|
||||
**Feature Components (src/components/features/):**
|
||||
- Business logic bevattend
|
||||
- Specifiek voor één feature
|
||||
- Groepeer per feature/domein
|
||||
|
||||
---
|
||||
|
||||
### File Naming Conventions
|
||||
|
||||
**Components:**
|
||||
```
|
||||
✅ Button.tsx # PascalCase
|
||||
✅ UserProfile.tsx
|
||||
❌ button.tsx
|
||||
❌ user-profile.tsx
|
||||
**Basic pattern:**
|
||||
```typescript
|
||||
<div className="space-y-4">
|
||||
{messages.map((msg) => (
|
||||
<div
|
||||
key={msg.id}
|
||||
className={msg.role === 'user' ? 'ml-auto' : 'mr-auto'}
|
||||
>
|
||||
<div className="bg-gray-200 p-3 rounded">
|
||||
{msg.content}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
```
|
||||
|
||||
**Hooks:**
|
||||
```
|
||||
✅ useAuth.ts # camelCase met 'use' prefix
|
||||
✅ useTodos.ts
|
||||
❌ UseAuth.ts
|
||||
❌ auth-hook.ts
|
||||
**With markdown rendering:**
|
||||
```typescript
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
|
||||
<div className="bg-gray-200 p-3 rounded">
|
||||
<ReactMarkdown>{msg.content}</ReactMarkdown>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Utilities:**
|
||||
```
|
||||
✅ formatDate.ts # camelCase
|
||||
✅ utils.ts
|
||||
✅ constants.ts
|
||||
```
|
||||
|
||||
**Types:**
|
||||
```
|
||||
✅ database.ts # camelCase
|
||||
✅ User.types.ts # optioneel: .types suffix
|
||||
**With message types:**
|
||||
```typescript
|
||||
{messages.map((msg) => (
|
||||
<div key={msg.id} className={msg.role === 'user' ? 'user-message' : 'ai-message'}>
|
||||
{msg.content}
|
||||
</div>
|
||||
))}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### .cursorrules Setup
|
||||
### Error Handling
|
||||
|
||||
**Maak .cursorrules in project root:**
|
||||
```markdown
|
||||
# Project: [Jouw Project Naam]
|
||||
**Structured error handling:**
|
||||
|
||||
## Tech Stack
|
||||
- Next.js 14 met App Router
|
||||
- TypeScript (strict mode)
|
||||
- Tailwind CSS
|
||||
- Supabase
|
||||
- React Query
|
||||
```typescript
|
||||
try {
|
||||
const response = await fetch('/api/chat', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ messages }),
|
||||
})
|
||||
|
||||
## File Structure
|
||||
- Components in src/components/
|
||||
- UI components in src/components/ui/
|
||||
- API routes in src/app/api/
|
||||
if (!response.ok) {
|
||||
throw new Error(`API error: ${response.status}`)
|
||||
}
|
||||
|
||||
## Code Conventions
|
||||
- Functional components only
|
||||
- Named exports (geen default exports)
|
||||
- Props interface boven component
|
||||
- Nederlandse comments
|
||||
// Handle streaming...
|
||||
} catch (error) {
|
||||
console.error('Chat error:', error)
|
||||
setError({
|
||||
message: 'Failed to send message',
|
||||
code: error instanceof Error ? error.message : 'unknown'
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Naming
|
||||
- Components: PascalCase (Button.tsx)
|
||||
- Hooks: camelCase met use prefix (useAuth.ts)
|
||||
- Utils: camelCase (formatDate.ts)
|
||||
|
||||
## Styling
|
||||
- Tailwind CSS classes
|
||||
- Geen inline styles
|
||||
- Responsive mobile-first
|
||||
|
||||
## TypeScript
|
||||
- Strict mode
|
||||
- Geen any types
|
||||
- Interfaces voor props
|
||||
- Types voor data
|
||||
|
||||
## Don'ts
|
||||
- Geen console.log in productie
|
||||
- Geen hardcoded strings
|
||||
- Geen unused imports
|
||||
**Error boundary:**
|
||||
```typescript
|
||||
<ErrorBoundary fallback={<div>Chat error occurred</div>}>
|
||||
<ChatComponent />
|
||||
</ErrorBoundary>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Git Best Practices
|
||||
### Loading States
|
||||
|
||||
**Commit Message Format:**
|
||||
```
|
||||
type: korte beschrijving
|
||||
**Skeleton loader:**
|
||||
```typescript
|
||||
function MessageSkeleton() {
|
||||
return (
|
||||
<div className="animate-pulse">
|
||||
<div className="bg-gray-300 h-4 rounded w-48 mb-2" />
|
||||
<div className="bg-gray-300 h-4 rounded w-64" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Types:
|
||||
- feat: nieuwe feature
|
||||
- fix: bug fix
|
||||
- refactor: code verbetering
|
||||
- docs: documentatie
|
||||
- style: formatting
|
||||
- test: tests toevoegen
|
||||
```
|
||||
|
||||
**Voorbeelden:**
|
||||
```bash
|
||||
git commit -m "feat: add user authentication with Supabase"
|
||||
git commit -m "fix: resolve hydration error in TodoList"
|
||||
git commit -m "docs: update README with setup instructions"
|
||||
```
|
||||
|
||||
**.gitignore essentials:**
|
||||
```
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Environment
|
||||
.env*.local
|
||||
|
||||
# Next.js
|
||||
.next/
|
||||
out/
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
{isLoading && <MessageSkeleton />}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Environment Variables
|
||||
### Input Validation
|
||||
|
||||
**Structuur:**
|
||||
```bash
|
||||
# .env.local (NOOIT committen!)
|
||||
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGci...
|
||||
OPENAI_API_KEY=sk-...
|
||||
**Validate before sending:**
|
||||
|
||||
# .env.example (WEL committen)
|
||||
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
|
||||
OPENAI_API_KEY=your-openai-key
|
||||
```typescript
|
||||
function handleSubmit(e: React.FormEvent) {
|
||||
e.preventDefault()
|
||||
|
||||
// Trim whitespace
|
||||
const trimmedInput = input.trim()
|
||||
|
||||
// Validate non-empty
|
||||
if (!trimmedInput) {
|
||||
setError('Message cannot be empty')
|
||||
return
|
||||
}
|
||||
|
||||
// Validate length
|
||||
if (trimmedInput.length > 1000) {
|
||||
setError('Message too long (max 1000 chars)')
|
||||
return
|
||||
}
|
||||
|
||||
// Send message
|
||||
handleSubmit(e)
|
||||
}
|
||||
```
|
||||
|
||||
**Regels:**
|
||||
- `NEXT_PUBLIC_` prefix = zichtbaar in browser
|
||||
- Zonder prefix = alleen server-side
|
||||
- Nooit secrets in `NEXT_PUBLIC_` vars
|
||||
|
||||
---
|
||||
|
||||
### README.md Template
|
||||
### Message Persistence
|
||||
|
||||
```markdown
|
||||
# Project Naam
|
||||
**Save to localStorage:**
|
||||
|
||||
Korte beschrijving van je project.
|
||||
```typescript
|
||||
const [messages, setMessages] = useState(() => {
|
||||
const saved = localStorage.getItem('chat_history')
|
||||
return saved ? JSON.parse(saved) : []
|
||||
})
|
||||
|
||||
## Features
|
||||
- Feature 1
|
||||
- Feature 2
|
||||
- Feature 3
|
||||
// Save whenever messages change
|
||||
useEffect(() => {
|
||||
localStorage.setItem('chat_history', JSON.stringify(messages))
|
||||
}, [messages])
|
||||
```
|
||||
|
||||
## Tech Stack
|
||||
- Next.js 14
|
||||
- TypeScript
|
||||
- Tailwind CSS
|
||||
- Supabase
|
||||
- Vercel AI SDK
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
- Node.js 18+
|
||||
- npm of yarn
|
||||
- Supabase account
|
||||
|
||||
### Installation
|
||||
|
||||
1. Clone de repository
|
||||
```bash
|
||||
git clone https://github.com/username/project.git
|
||||
cd project
|
||||
```
|
||||
|
||||
2. Installeer dependencies
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
3. Maak .env.local (zie .env.example)
|
||||
|
||||
4. Start development server
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
Zie `.env.example` voor benodigde variabelen.
|
||||
|
||||
## Deployment
|
||||
Deployed op Vercel: [productie-url]
|
||||
|
||||
## Documentatie
|
||||
- [PROMPT-LOG.md](docs/PROMPT-LOG.md)
|
||||
- [AI-DECISIONS.md](docs/AI-DECISIONS.md)
|
||||
**Save to database:**
|
||||
```typescript
|
||||
const saveMessage = async (message: Message) => {
|
||||
await fetch('/api/messages', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
content: message.content,
|
||||
role: message.role,
|
||||
userId: user.id,
|
||||
}),
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tools
|
||||
- Vercel AI SDK
|
||||
- React Markdown
|
||||
- Cursor
|
||||
- Git
|
||||
- GitHub
|
||||
- TypeScript
|
||||
|
||||
---
|
||||
|
||||
## Lesopdracht (2 uur)
|
||||
## Lesopdracht (2 uur, klassikaal)
|
||||
|
||||
### Setup Je Eindproject
|
||||
### Build Professional Chat Interface
|
||||
|
||||
**Deel 1: Project Structuur (45 min)**
|
||||
**Groepsdiscussie (15 min):**
|
||||
Bespreek klassikaal de Vercel AI SDK ervaringen uit Les 13 - welke tool calling patterns werkten goed?
|
||||
|
||||
1. Maak nieuw Next.js project:
|
||||
```bash
|
||||
npx create-next-app@latest mijn-eindproject --typescript --tailwind --app
|
||||
```
|
||||
**Deel 1: Chat Component (45 min)**
|
||||
|
||||
2. Maak de mappenstructuur:
|
||||
- src/components/ui/
|
||||
- src/components/layout/
|
||||
- src/components/features/
|
||||
- src/lib/
|
||||
- src/hooks/
|
||||
- src/types/
|
||||
- docs/
|
||||
Build components/ChatInterface.tsx:
|
||||
1. Use useChat hook
|
||||
2. Render messages with proper styling
|
||||
3. User vs AI message styling
|
||||
4. Input form with validation
|
||||
5. Tailwind + shadcn/ui components
|
||||
|
||||
3. Maak placeholder files:
|
||||
- src/lib/supabase.ts
|
||||
- src/lib/utils.ts
|
||||
**Deel 2: Markdown & Error Handling (30 min)**
|
||||
|
||||
**Deel 2: Configuratie (30 min)**
|
||||
1. Install react-markdown: `npm install react-markdown`
|
||||
2. Render AI responses with markdown
|
||||
3. Add error boundary
|
||||
4. Show error messages to user
|
||||
5. Proper loading states
|
||||
|
||||
1. Maak .cursorrules met jouw conventies
|
||||
2. Maak .env.example
|
||||
3. Update .gitignore
|
||||
4. Maak README.md met template
|
||||
**Deel 3: UX Improvements (30 min)**
|
||||
|
||||
**Deel 3: Git Setup (25 min)**
|
||||
1. Auto-scroll to latest message
|
||||
2. Disable input while loading
|
||||
3. Show message count/token usage
|
||||
4. Add clear chat history button
|
||||
5. Save messages to localStorage
|
||||
|
||||
1. git init
|
||||
2. Initial commit met goede message
|
||||
3. Push naar GitHub
|
||||
4. Check: .env.local NIET gecommit?
|
||||
**Deel 4: Testing (15 min)**
|
||||
|
||||
**Deel 4: Documentatie Start (20 min)**
|
||||
|
||||
Maak in docs/:
|
||||
- PROJECT-BRIEF.md (beschrijving eindproject)
|
||||
- PROMPT-LOG.md (leeg template)
|
||||
- AI-DECISIONS.md (leeg template)
|
||||
Test chat interface locally with various inputs and error scenarios.
|
||||
|
||||
### Deliverable
|
||||
- GitHub repository met correcte structuur
|
||||
- .cursorrules file
|
||||
- README.md
|
||||
- docs/ folder met templates
|
||||
- Werkende chat interface component
|
||||
- Markdown rendering working
|
||||
- Error handling implemented
|
||||
- LocalStorage persistence
|
||||
- GitHub commit with chat UI
|
||||
|
||||
---
|
||||
|
||||
## Huiswerk (2 uur)
|
||||
|
||||
### Bouw Project Foundation
|
||||
### Integrate Chat into Your Project
|
||||
|
||||
**Deel 1: Base Components (1 uur)**
|
||||
**Deel 1: Project Integration (1 uur)**
|
||||
|
||||
Maak basis UI components met AI hulp:
|
||||
- src/components/ui/Button.tsx
|
||||
- src/components/ui/Input.tsx
|
||||
- src/components/ui/Card.tsx
|
||||
- src/components/layout/Header.tsx
|
||||
- src/components/layout/Footer.tsx
|
||||
1. Add chat component to your app
|
||||
2. Connect to your API route with tools
|
||||
3. Style to match your design
|
||||
4. Test with actual tools/integrations
|
||||
5. Fix any bugs
|
||||
|
||||
Requirements:
|
||||
- TypeScript interfaces voor props
|
||||
- Tailwind styling
|
||||
- Responsive design
|
||||
- Volg je .cursorrules
|
||||
**Deel 2: Enhanced Features (30 min)**
|
||||
|
||||
**Deel 2: Supabase Setup (30 min)**
|
||||
Add one of these:
|
||||
- Message copy button
|
||||
- Regenerate response option
|
||||
- Clear history confirmation
|
||||
- Export chat history
|
||||
- Message timestamps
|
||||
|
||||
1. Maak Supabase project (of hergebruik van Les 9)
|
||||
2. Configureer src/lib/supabase.ts
|
||||
3. Voeg env vars toe aan .env.local
|
||||
4. Test connectie
|
||||
**Deel 3: Performance & Polish (30 min)**
|
||||
|
||||
**Deel 3: Eerste Feature (30 min)**
|
||||
|
||||
Kies je eindproject en implementeer 1 basisfeature:
|
||||
- Recipe Generator: ingredient input form
|
||||
- Budget Buddy: expense entry form
|
||||
- Travel Planner: destination search
|
||||
|
||||
Commit en push!
|
||||
1. Optimize re-renders (useMemo, useCallback)
|
||||
2. Virtual scrolling for long chats
|
||||
3. Better accessibility (keyboard nav)
|
||||
4. Mobile responsive tweaks
|
||||
5. Update docs/AI-DECISIONS.md
|
||||
|
||||
### Deliverable
|
||||
- Werkende UI components
|
||||
- Supabase connectie
|
||||
- 1 basic feature
|
||||
- Alle commits met goede messages
|
||||
|
||||
---
|
||||
|
||||
## 💡 Eindopdracht
|
||||
|
||||
Dit is een goed moment om te starten met **deelopdracht 1** van je eindopdracht. De setup die je vandaag maakt kun je direct gebruiken voor je eindproject. Bespreek je projectidee met de docent als je feedback wilt.
|
||||
- Chat fully integrated in project
|
||||
- Enhanced features implemented
|
||||
- Performance optimized
|
||||
- GitHub commits with improvements
|
||||
|
||||
---
|
||||
|
||||
## Leerdoelen
|
||||
|
||||
Na deze les kan de student:
|
||||
- Een professionele project structuur opzetten
|
||||
- File naming conventions toepassen
|
||||
- Een effectieve .cursorrules file schrijven
|
||||
- Git best practices volgen
|
||||
- Environment variables correct beheren
|
||||
- Een README.md schrijven
|
||||
- Project documentatie structureren
|
||||
- useChat hook volledig begrijpen en gebruiken
|
||||
- Professionele chat UI patterns implementeren
|
||||
- Markdown rendering in chat messages
|
||||
- Error handling en error boundaries toepassen
|
||||
- Loading states en skeletons bouwen
|
||||
- User input valideren en sanitizen
|
||||
- Message persistence (localStorage/DB)
|
||||
- Accessibility in chat interfaces verbeteren
|
||||
- Performance optimizations toepassen
|
||||
- Complete chat feature in Next.js app integreren
|
||||
|
||||
Reference in New Issue
Block a user