diff --git a/app/(marketing)/components/hero-section-client.tsx b/app/(marketing)/components/hero-section-client.tsx index 17e5f4f..9a28d19 100644 --- a/app/(marketing)/components/hero-section-client.tsx +++ b/app/(marketing)/components/hero-section-client.tsx @@ -3,20 +3,25 @@ import { HeroSection } from '@/components/ui/hero-section-2' interface HeroSectionClientProps { - logo: { + logo?: { url: string alt: string text?: string } slogan?: string title: React.ReactNode - subtitle: string + subtitle: string | React.ReactNode + punchline?: string | React.ReactNode callToAction: { text: string href: string } + secondaryCallToAction?: { + text: string + href: string + } backgroundImage: string - contactInfo: { + contactInfo?: { website: string phone: string address: string diff --git a/app/(marketing)/page.tsx b/app/(marketing)/page.tsx index 53e86f4..f7e530a 100644 --- a/app/(marketing)/page.tsx +++ b/app/(marketing)/page.tsx @@ -17,6 +17,7 @@ import { getContent } from '@/lib/content/loader' import type { MetadataContent } from '@/content/schemas/manifesto' import { HeroSectionClient } from './components/hero-section-client' import { BuildTimeline } from './components/build-timeline' +import { WhyMe } from '@/components/ui/why-me' // Generate metadata for SEO export async function generateMetadata(): Promise { @@ -70,13 +71,31 @@ export async function generateMetadata(): Promise { } // Content interfaces -interface StatementContent { - hero: { - quote: string - attribution: string - attributionContext: string - subtitle: string +interface HeroContent { + title: { + main: string + accent: string } + subtitle: string + punchline: { + question: string + answer: string + } + callToAction: { + primary: { + text: string + href: string + } + secondary: { + text: string + href: string + } + } + backgroundImage: string +} + +interface StatementContent { + hero: HeroContent } interface Feature { @@ -111,35 +130,32 @@ export default async function HomePage() { // Load content const manifestoContent = await getContent('nl', 'manifesto') const timelineContent = await getContent('nl', 'timeline') + const heroContent = manifestoContent.hero return ( <> {/* Hero Section */} - Software on Demand -
- Van €100k naar €200 + {heroContent.title.main}{' '} + + {heroContent.title.accent} + } - subtitle="Enterprise software hoeft niet meer €100.000+ per jaar te kosten. AI-powered development verkort dit naar 4 weken en €200 totale kosten. Dit EPD prototype is het levende bewijs." - callToAction={{ - text: "PROBEER HET PROTOTYPE →", - href: "/login" - }} - backgroundImage="https://images.unsplash.com/photo-1451187580459-43490279c0fa?q=80&w=2072&auto=format&fit=crop" - contactInfo={{ - website: "aispeedrun.nl", - phone: "Demo Project", - address: "Build in Public" - }} + subtitle={heroContent.subtitle} + punchline={ + <> + {heroContent.punchline.question} +
+
+ {heroContent.punchline.answer} + + } + callToAction={heroContent.callToAction.primary} + secondaryCallToAction={heroContent.callToAction.secondary} + backgroundImage={heroContent.backgroundImage} /> {/* Statement Section - Software on Demand */} @@ -181,6 +197,9 @@ export default async function HomePage() { + {/* About Me Section */} + + {/* CTA Section */}
diff --git a/app/globals.css b/app/globals.css index 2e0b5f9..6814778 100644 --- a/app/globals.css +++ b/app/globals.css @@ -211,16 +211,40 @@ body { outline-offset: 2px; } +/* Floating animation for expertise badges */ +@keyframes float { + 0%, 100% { + transform: translateY(0px) rotate(0deg); + } + 25% { + transform: translateY(-5px) rotate(0.5deg); + } + 50% { + transform: translateY(-3px) rotate(-0.5deg); + } + 75% { + transform: translateY(-7px) rotate(0.3deg); + } +} + +.animate-float { + animation: float 5s ease-in-out infinite; +} + /* Reduced motion support */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { - animation-duration: 0.01ms !important; + animation-duration: 0.02ms !important; animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; + transition-duration: 0.03ms !important; scroll-behavior: auto !important; } + + .animate-float { + animation: none !important; + } } /* AI source highlighting (gebruikt in Intake editor) */ diff --git a/components/ui/hero-section-2.tsx b/components/ui/hero-section-2.tsx index b702294..f0ddb74 100644 --- a/components/ui/hero-section-2.tsx +++ b/components/ui/hero-section-2.tsx @@ -37,13 +37,18 @@ interface HeroSectionProps { }; slogan?: string; title: React.ReactNode; - subtitle: string; + subtitle: string | React.ReactNode; + punchline?: string | React.ReactNode; callToAction: { text: string; href: string; }; + secondaryCallToAction?: { + text: string; + href: string; + }; backgroundImage: string; - contactInfo: { + contactInfo?: { website: string; phone: string; address: string; @@ -52,7 +57,7 @@ interface HeroSectionProps { } const HeroSection = React.forwardRef( - ({ className, logo, slogan, title, subtitle, callToAction, backgroundImage, contactInfo, ...props }, ref) => { + ({ className, logo, slogan, title, subtitle, punchline, callToAction, secondaryCallToAction, backgroundImage, contactInfo, ...props }, ref) => { // Animation variants for the container to orchestrate children animations const containerVariants = { @@ -108,36 +113,64 @@ const HeroSection = React.forwardRef( - + {title} - + {subtitle} - - - {callToAction.text} - + + + {punchline && ( + +
+ {punchline} +
+
+ )} + + + + {callToAction.text} + + {secondaryCallToAction && ( + + {secondaryCallToAction.text} + + )} +
{/* Bottom Section: Footer Info */} - -
-
- - {contactInfo.website} + {contactInfo && ( + +
+
+ + {contactInfo.website} +
+
+ + {contactInfo.phone} +
+
+ + {contactInfo.address} +
-
- - {contactInfo.phone} -
-
- - {contactInfo.address} -
-
- + + )}
{/* Right Side: Image with Clip Path Animation */} diff --git a/components/ui/scroll-reveal.tsx b/components/ui/scroll-reveal.tsx new file mode 100644 index 0000000..cdfbbb1 --- /dev/null +++ b/components/ui/scroll-reveal.tsx @@ -0,0 +1,66 @@ +'use client' + +import { useEffect, useRef, useState, ReactNode } from 'react' + +interface ScrollRevealProps { + children: ReactNode + direction?: 'up' | 'down' | 'left' | 'right' + delay?: number + className?: string +} + +export function ScrollReveal({ + children, + direction = 'up', + delay = 0, + className = '', +}: ScrollRevealProps) { + const [isVisible, setIsVisible] = useState(false) + const ref = useRef(null) + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setIsVisible(true) + observer.disconnect() + } + }, + { + threshold: 0.1, + rootMargin: '50px', + } + ) + + if (ref.current) { + observer.observe(ref.current) + } + + return () => { + observer.disconnect() + } + }, []) + + const directionClasses = { + up: 'translate-y-8', + down: '-translate-y-8', + left: 'translate-x-8', + right: '-translate-x-8', + } + + return ( +
+ {children} +
+ ) +} diff --git a/components/ui/why-me.tsx b/components/ui/why-me.tsx new file mode 100644 index 0000000..5378301 --- /dev/null +++ b/components/ui/why-me.tsx @@ -0,0 +1,117 @@ +'use client' + +import { useEffect, useState } from 'react' +import Image from 'next/image' +import { ScrollReveal } from '@/components/ui/scroll-reveal' +import aboutContent from '@/content/nl/about.json' + +export function WhyMe() { + const [mounted, setMounted] = useState(false) + const { title, subtitle, image, paragraphs, stats, expertise } = aboutContent + + useEffect(() => { + setMounted(true) + }, []) + + return ( +
+ {/* Decorative elements */} +
+
+ +
+
+ {/* Left: Text Content */} + +
+

+ {title} +

+ +

+ {subtitle} +

+ +
+ {paragraphs.map((paragraph, index) => ( +

{paragraph}

+ ))} +
+ +
+ {stats.map((stat, index) => ( +
+
{stat.value}
+
{stat.label}
+
+ ))} +
+
+
+ + {/* Right: Photo */} + +
+ {/* Optional subtle border/frame effect */} +
+ +
+ {image.alt} +
+
+ +
+ + {/* FASE 2: Expertise Section - Full Width with Floating Animation */} + {expertise && ( +
+ +

+ {expertise.label} +

+

+ De tools en methoden waarmee ik jouw AI-uitdaging aanpak +

+
+ +
+ {expertise.items.map((item, index) => ( +
+ {item.name} + + {item.category} + +
+ ))} +
+
+ )} +
+
+ ) +} diff --git a/content/nl/about.json b/content/nl/about.json new file mode 100644 index 0000000..675efe3 --- /dev/null +++ b/content/nl/about.json @@ -0,0 +1,95 @@ +{ + "title": "Over mij", + "subtitle": "AI-gedreven development in de praktijk", + "image": { + "src": "/images/colin-office-6.webp", + "alt": "Colin van der Heijden - AI Development Specialist" + }, + "paragraphs": [ + "Na jaren in traditionele software development te hebben gewerkt, zag ik hoe bedrijven vast liepen in eindeloze implementatietrajecten en vendor lock-in. De belofte van AI-powered development klonk te mooi om waar te zijn - totdat ik het zelf ging testen.", + "Dit EPD prototype is geen marketingpraatje, maar een hands-on bewijs van wat er mogelijk is. Elke regel code, elk design decision, elke feature - allemaal gedocumenteerd en transparant. Want de beste manier om te laten zien wat AI-development kan, is het gewoon doen." + ], + "stats": [ + { + "value": "4 weken", + "label": "Van concept tot werkend prototype" + }, + { + "value": "€200", + "label": "Totale build cost" + }, + { + "value": "10+ jaar", + "label": "Ervaring in software development" + }, + { + "value": "100%", + "label": "Transparant build in public" + } + ], + "expertise": { + "label": "Tech Stack & Expertise", + "items": [ + { + "name": "Gezond Verstand", + "category": "Mindset", + "highlight": true + }, + { + "name": "Supabase", + "category": "Backend", + "highlight": false + }, + { + "name": "Goeie werkethos", + "category": "Mindset", + "highlight": true + }, + { + "name": "Gemini AI", + "category": "AI Tool", + "highlight": false + }, + { + "name": "Claude AI", + "category": "AI Tool", + "highlight": false + }, + { + "name": "Cursor IDE", + "category": "Development", + "highlight": false + }, + { + "name": "No Bullshit", + "category": "Mindset", + "highlight": true + }, + { + "name": "Vercel", + "category": "Hosting", + "highlight": false + }, + { + "name": "Supabase", + "category": "Database", + "highlight": false + }, + { + "name": "Bak ervaring", + "category": "Integration", + "highlight": false + }, + { + "name": "Handson", + "category": "Mindset", + "highlight": true + }, + { + "name": "Next.js", + "category": "Frontend", + "highlight": false + } + ] + } +} diff --git a/content/nl/manifesto.json b/content/nl/manifesto.json index c177b72..d48de9a 100644 --- a/content/nl/manifesto.json +++ b/content/nl/manifesto.json @@ -1,9 +1,25 @@ { "hero": { - "quote": "Software is eating the world, but AI is going to eat software", - "attribution": "Jensen Huang, CEO van Nvidia", - "attributionContext": "zei dit tijdens zijn keynote op GTC in maart 2024", - "subtitle": "Nu, in 2025, zien we het gebeuren. En bijna niemand heeft het door." + "title": { + "main": "\"Staat op de roadmap voor Q3 2026\"", + "accent": "Dat hoef je nooit meer te horen." + }, + "subtitle": "Ik bouw live een EPD, stap voor stap, open en transparant. Laten we samen kijken hoe ver we komen.", + "punchline": { + "question": "De vraag is niet óf AI softwareontwikkeling verandert.", + "answer": "De vraag is: accepteer je de beperkingen, of kies je voor mogelijkheden?" + }, + "callToAction": { + "primary": { + "text": "🔵 VOLG DE AI SPEEDRUN", + "href": "#timeline" + }, + "secondary": { + "text": "⚪ WAAROM EEN EPD?", + "href": "#over" + } + }, + "backgroundImage": "https://images.unsplash.com/photo-1451187580459-43490279c0fa?q=80&w=2072&auto=format&fit=crop" }, "sections": [ { diff --git a/docs/specs/bouwplan-login-refactor-v1.0.md b/docs/specs/bouwplan-login-refactor-v1.0.md new file mode 100644 index 0000000..69813f8 --- /dev/null +++ b/docs/specs/bouwplan-login-refactor-v1.0.md @@ -0,0 +1,438 @@ +# 🚀 Bouwplan — Login Refactor: Email + Wachtwoord Primair + +**Projectnaam:** Mini EPD Demo Platform - Login Optimalisatie +**Versie:** v1.0 +**Datum:** 18-01-2025 +**Auteur:** Colin (met Claude Code) +**Scope:** Login page herontwerp (Email+Password primair, Magic Link secundair) + +--- + +## 1. Doel en Context + +🎯 **Doel:** De login flow optimaliseren voor een demo platform door Email + Wachtwoord de primaire methode te maken in plaats van Magic Link. + +📘 **Toelichting:** +Het huidige login scherm heeft Magic Link als primaire methode, maar voor een **demo platform** is dit suboptimaal omdat prospects direct willen inloggen zonder email roundtrip. We maken Email + Password de primaire methode en verplaatsen Magic Link naar een secundaire optie. + +**Huidige situatie:** +- Magic Link is default (toggle nodig voor demo login) +- Demo credentials verborgen achter extra click +- 2-3 extra clicks voor demo users +- Conditional rendering met `showDemoLogin` state + +**Gewenste situatie:** +- Email + Password formulier als primair scherm +- One-click demo login prominent zichtbaar +- Magic Link als alternatieve optie onderaan +- Alle opties altijd zichtbaar (geen toggle) + +**Context:** +- Dit is een DEMO platform, geen productie EPD +- Primaire use case: Prospects willen snel kijken +- Secundaire use case: Serieuze trial users maken eigen account +- Magic Link blijft beschikbaar voor zero-password signup + +--- + +## 2. Uitgangspunten + +### 2.2 Projectkaders + +- **Tijd:** 2-3 uur voor volledige implementatie + testing +- **Team:** 1 developer (zelfstandig uit te voeren) +- **Demo accounts:** Bestaande demo users blijven ongewijzigd +- **Breaking changes:** GEEN - Alle bestaande auth flows blijven werken +- **Deployment:** Auto-deploy via Vercel na git push + +### 2.3 Programmeer Uitgangspunten + +**Code Quality Principles:** +- ✅ **DRY:** Hergebruik bestaande auth functies (`loginWithPassword`, `loginWithMagicLink`) +- ✅ **KISS:** Eenvoudige layout refactor, geen complexe state management +- ✅ **SOC:** UI changes in page.tsx, auth logic blijft in lib/auth/client.ts +- ✅ **YAGNI:** Alleen login UI optimalisatie, geen extra conversion features + +**Security:** +- Demo credentials blijven in info box (niet hardcoded in code) +- Bestaande Supabase auth flows blijven ongewijzigd +- Geen nieuwe environment variables nodig + +**Bestaande Bestanden (NIET wijzigen):** +``` +lib/auth/client.ts - Auth functies +lib/auth/server.ts - Server auth +middleware.ts - Route protection +app/auth/callback/route.ts - Magic link callback +app/auth/logout/route.ts - Logout handler +components/ui/button.tsx - UI components +``` + +**Te Wijzigen Bestanden:** +``` +app/login/page.tsx - Volledige UI refactor +``` + +--- + +## 3. Epics & Stories Overzicht + +🎯 **Doel:** De bouw opdelen in logische epics (fases) met stories (subfases). + +| Epic ID | Titel | Doel | Status | Stories | Geschatte Tijd | +|---------|-------|------|--------|---------|----------------| +| E1 | Login UI Refactor | Email+Password primair maken | ⏳ To Do | 3 | 1-2 uur | +| E2 | Demo UX Verbetering | One-click demo + betere copy | ⏳ To Do | 2 | 30 min | +| E3 | Testing & Verificatie | Alle flows testen | ⏳ To Do | 2 | 30 min | + +**Totale schatting:** 2-3 uur werk + +--- + +## 4. Epics & Stories (Uitwerking) + +### Epic 1 — Login UI Refactor + +**Epic Doel:** Email + Password formulier wordt de primaire login methode zonder toggle logic. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | Story Points | +|----------|--------------|---------------------|--------|------------------|-----------------| +| E1.S1 | Verwijder conditional toggle logic | `showDemoLogin` state verwijderd, geen toggle buttons meer | ⏳ | — | 2 | +| E1.S2 | Herstructureer page layout | Email+Password bovenaan, Magic Link onderaan met divider | ⏳ | E1.S1 | 3 | +| E1.S3 | Update copy & labels | Primaire form heet "Login", Magic Link "Alternatieve optie" | ⏳ | E1.S2 | 1 | + +**Technical Notes:** + +**E1.S1 - Toggle Logic Verwijderen:** +```typescript +// VERWIJDER: +const [showDemoLogin, setShowDemoLogin] = useState(false) + +// VERWIJDER buttons: + + +``` + +**E1.S2 - Layout Herstructureren:** +``` +NIEUWE STRUCTUUR: +┌─────────────────────────────────────────┐ +│ Header: "Login" │ +├─────────────────────────────────────────┤ +│ [QuickDemoButton - zie E2.S1] │ +│ │ +│ ─── of vul handmatig in ─── │ +│ │ +│ Email: [________________] │ +│ Password: [________________] │ +│ [Login Button] │ +│ │ +│ [Demo Credentials Info Box - E2.S2] │ +│ │ +│ ─────────── of ─────────── │ +│ │ +│ Gebruik Magic Link (geen wachtwoord) │ +│ Email: [________________] │ +│ [Stuur Magic Link] │ +└─────────────────────────────────────────┘ +``` + +**E1.S3 - Copy Updates:** +```typescript +// OUD → NIEUW +"🔑 Demo Account Login" → "Login" +"📧 Login met Magic Link" → "Of gebruik Magic Link" +"Login met Demo Account" → [VERWIJDERD - QuickDemoButton vervangt dit] +"Snelle Demo Login" → "🚀 Start Demo" (in QuickDemoButton) +``` + +--- + +### Epic 2 — Demo UX Verbetering + +**Epic Doel:** Demo gebruikers kunnen met één click inloggen zonder formulier in te vullen. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | Story Points | +|----------|--------------|---------------------|--------|------------------|-----------------| +| E2.S1 | Implementeer QuickDemoButton | One-click demo login zonder form invullen | ⏳ | E1.S2 | 3 | +| E2.S2 | Voeg credentials info box toe | Demo credentials zichtbaar voor manual login | ⏳ | E1.S2 | 2 | + +**Technical Notes:** + +**E2.S1 - QuickDemoButton Component:** +```typescript +// Inline component in app/login/page.tsx +// (of aparte component indien herbruikbaar elders) + +function QuickDemoButton() { + const [loading, setLoading] = useState(false) + const router = useRouter() + + async function handleQuickDemo() { + setLoading(true) + try { + const result = await loginWithPassword( + 'demo@mini-ecd.demo', + 'Demo2024!' + ) + + if (result.success) { + setMessage({ + type: 'success', + text: 'Ingelogd! Redirect naar EPD...' + }) + setTimeout(() => router.push('/epd/clients'), 1000) + } + } catch (error) { + setMessage({ + type: 'error', + text: 'Login mislukt. Probeer opnieuw.' + }) + setLoading(false) + } + } + + return ( + + ) +} +``` + +**E2.S2 - Credentials Info Box:** +```typescript +// Info card onder password form, boven Magic Link divider + +
+

+ Demo Account Credentials: +

+
+

📧 Email: demo@mini-ecd.demo

+

🔒 Wachtwoord: Demo2024!

+
+

+ 💡 Of gebruik de "Start Demo" knop voor directe toegang +

+
+``` + +--- + +### Epic 3 — Testing & Verificatie + +**Epic Doel:** Alle login flows werken correct na refactor zonder regressies. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | Story Points | +|----------|--------------|---------------------|--------|------------------|-----------------| +| E3.S1 | Test alle login flows | Alle 3 methoden werken zonder errors | ⏳ | E2.S2 | 2 | +| E3.S2 | Responsive & accessibility check | Werkt op mobile, keyboard navigatie OK | ⏳ | E3.S1 | 1 | + +**Technical Notes:** + +**E3.S1 - Login Flow Test Scenarios:** + +| Test Case | Scenario | Expected Result | Status | +|-----------|----------|-----------------|--------| +| TC1 | Click "Start Demo" button | Direct inloggen → redirect /epd/clients | ⏳ | +| TC2 | Manual login (demo credentials) | Formulier submit → success → redirect | ⏳ | +| TC3 | Magic link (geldig email) | "Check je email" message → email ontvangen | ⏳ | +| TC4 | Invalid password | Error message: "Ongeldige inloggegevens" | ⏳ | +| TC5 | Empty fields | Validation error | ⏳ | +| TC6 | Network error | User-friendly error message | ⏳ | + +**E3.S2 - Responsive & Accessibility Checklist:** + +**Responsive:** +- [ ] 320px viewport (iPhone SE): Layout niet broken +- [ ] 768px viewport (iPad): Twee-kolom layout werkt +- [ ] 1920px viewport (Desktop): Maximale breedte begrensd + +**Accessibility:** +- [ ] Tab-order logisch: QuickDemo → Email → Password → Login → MagicLink Email → Send +- [ ] Focus states zichtbaar (outline/ring) +- [ ] Error messages hebben `role="alert"` +- [ ] Buttons hebben duidelijke labels +- [ ] Contrast ratio > 4.5:1 (WCAG AA) +- [ ] Screen reader test: NVDA/VoiceOver leest alles voor + +**Browser Testing:** +- [ ] Chrome (latest) +- [ ] Firefox (latest) +- [ ] Safari (macOS + iOS) +- [ ] Edge (latest) + +--- + +## 5. Kwaliteit & Testplan + +🎯 **Doel:** Vastleggen hoe de kwaliteit van de refactor wordt geborgd. + +### Manual Test Checklist (voor deployment) + +**Happy Flows:** +- [ ] Quick demo button: Click → Loading state → Success → Redirect /epd/clients +- [ ] Manual email+password: Type credentials → Submit → Redirect +- [ ] Magic link: Enter email → Submit → "Check email" message → Email ontvangen + +**Error Flows:** +- [ ] Wrong password: Error message "Ongeldige inloggegevens" +- [ ] Invalid email format: Validation error +- [ ] Network timeout: User-friendly error +- [ ] Rate limit (4 emails/hour): Supabase error handled + +**UI/UX:** +- [ ] Loading states show spinners +- [ ] Success messages turn teal-50 background +- [ ] Error messages turn red-50 background +- [ ] All text readable (contrast check) +- [ ] No console errors/warnings +- [ ] No layout shift during loading + +**Regression Testing:** +- [ ] Existing demo accounts still work +- [ ] `/auth/callback` magic link flow unchanged +- [ ] Middleware still protects `/epd/*` routes +- [ ] Logout still works (`/auth/logout`) +- [ ] Session refresh in middleware works + +--- + +## 6. Risico's & Mitigatie + +🎯 **Doel:** Risico's vroeg signaleren en voorzien van oplossingen. + +| Risico | Kans | Impact | Mitigatie | Owner | +|--------|------|--------|-----------|-------| +| Breaking change in login flow | Laag | Hoog | - Behoud alle bestaande auth functies
- Test beide flows grondig
- Rollback plan ready | Developer | +| Demo credentials exposure | Middel | Laag | - Info box toont credentials (is OK voor demo)
- Geen hardcoded passwords in source
- RLS policies beschermen database | Developer | +| Magic link users verward | Middel | Laag | - Duidelijke "Of gebruik Magic Link" sectie
- Behoud alle bestaande UX voor magic link | Developer | +| Mobile layout breaks | Laag | Middel | - Test op 320px viewport
- Use responsive Tailwind classes
- Max-width container | Developer | +| Supabase rate limit tijdens testing | Hoog | Laag | - Use demo account voor testing (geen magic link)
- Test magic link max 1x per test run | Developer | +| Accessibility regression | Middel | Middel | - Tab-order testing
- Screen reader check
- WCAG contrast check | Developer | + +--- + +## 7. Definition of Done + +**Epic 1-3 zijn compleet wanneer:** + +✅ **Functional Requirements:** +- Email + Password is primair formulier (bovenaan pagina) +- Quick demo button werkt (one-click login) +- Magic Link optie blijft beschikbaar (onderaan) +- Demo credentials info box zichtbaar +- Alle 3 login methoden getest en werkend + +✅ **Quality Requirements:** +- Geen console errors/warnings +- Mobile responsive (320px - 1920px) +- Accessible (keyboard nav + screen reader) +- Loading states correct +- Error messages user-friendly + +✅ **Code Quality:** +- Bestaande auth functies ongewijzigd +- Clean code (geen commented code) +- Consistent Tailwind styling +- Type-safe (TypeScript errors = 0) + +✅ **Documentation:** +- Git commit message: `feat: Login UI refactor - Email+Password primair` +- Code comments voor complexe logica +- Dit bouwplan bijgewerkt met "✅ Gereed" status + +✅ **Deployment:** +- Lokaal getest (npm run dev) +- Git commit + push +- Vercel auto-deploy succesvol +- Production smoke test uitgevoerd + +--- + +## 8. Implementatie Volgorde + +**Aanbevolen volgorde:** + +1. **E1.S1** - Verwijder toggle logic (15 min) + - Clean up `showDemoLogin` state + - Verwijder toggle buttons + +2. **E1.S2** - Herstructureer layout (30 min) + - Email+Password form bovenaan + - Dividers toevoegen + - Magic Link onderaan + +3. **E1.S3** - Update copy (10 min) + - Alle labels updaten + - Verwarrende tekst verwijderen + +4. **E2.S1** - QuickDemoButton (20 min) + - Component implementeren + - Loading states + - Error handling + +5. **E2.S2** - Credentials info box (10 min) + - Styled info card + - Demo credentials display + +6. **E3.S1** - Test alle flows (20 min) + - Happy flows + - Error flows + - Regression tests + +7. **E3.S2** - Responsive + A11y (15 min) + - Mobile viewport test + - Keyboard navigation + - Screen reader check + +**Total: ~2 uur** + +--- + +## 9. Referenties + +**Mission Control Documents:** +- **PRD:** `docs/specs/prd-mini-ecd-v1.2.md` +- **FO:** `docs/specs/fo-mini-ecd-v2.md` +- **TO:** `docs/specs/to-mini-ecd-v1_2.md` +- **Auth Setup:** `docs/AUTH_SETUP.md` + +**Code References:** +- Login page: `app/login/page.tsx` (TE WIJZIGEN) +- Auth client: `lib/auth/client.ts` (ONGEWIJZIGD) +- Auth server: `lib/auth/server.ts` (ONGEWIJZIGD) +- Middleware: `middleware.ts` (ONGEWIJZIGD) + +**External Resources:** +- Repository: `https://github.com/[org]/15-mini-epd-prototype` +- Deployment: Vercel (auto-deploy on push) +- Supabase Project: `dqugbrpwtisgyxscpefg` (EU region) + +--- + +## 10. Versiehistorie + +| Versie | Datum | Auteur | Wijziging | +|--------|-------|--------|-----------| +| v1.0 | 18-01-2025 | Colin | Initiële versie - Login refactor bouwplan | + +--- + +**Status:** ⏳ Ready for Implementation +**Next Steps:** Start met E1.S1 (toggle logic verwijderen) diff --git a/public/images/colin-office-6.webp b/public/images/colin-office-6.webp new file mode 100644 index 0000000..88c1a11 Binary files /dev/null and b/public/images/colin-office-6.webp differ