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,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 @@
%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,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"]
}