feat: Epic 1 completion - Marketing refactor met timeline en Bento Grid login

Epic 1 Stories (E1.S1 t/m E1.S6):

 E1.S1 - Verwijder EPD demo pagina
- Removed /epd route en credentials-box component
- Updated navigation: EPD Prototype → Login link
- Removed demo_users references from middleware en auth libs
- Cleaned up TypeScript errors in hero-section-2

 E1.S2 - Homepage vereenvoudigen
- Replaced lange manifesto (8000+ woorden) met statement section
- Added problem-solution-proof format (3 paragrafen)
- Removed comparison table, experiment CTA, manifesto content
- Timeline placeholder toegevoegd (completed in E1.S3)

 E1.S3 - Timeline component integreren
- Created BuildTimeline component (Aceternity UI pattern)
- Added timeline.json met 4 weken data (Week 1 completed)
- Features per week met icons, metrics, achievements
- Scroll-based animation met Framer Motion
- Responsive design (sticky titles, mobile/desktop layouts)

 E1.S4 - Timeline content structuur
- Structured JSON data in content/nl/timeline.json
- 15 Lucide icons voor feature types
- Week status badges (completed/in_progress/planned)
- Time savings display (< 5 sec vs 30 min)

 E1.S5 - Login pagina refactor
- Split-screen layout (60% features, 40% login)
- Teal gradient showcase met 4 feature cards
- Time savings badges per feature
- Responsive stack layout voor mobile
- Footer met AI Speedrun branding

 E1.S6 - Bento Grid showcase
- Replaced feature grid met Bento Grid layout
- Variable card sizes voor visual hierarchy (col-span-2, col-span-1)
- Dark slate background (from-slate-900)
- Gradient backgrounds per card (teal, amber, purple)
- Stats footer met 3 key metrics (90%+, < 5 sec, 4 weken)
- Hover animations en glassmorphism effects

🎨 Design System:
- Teal-first brand colors (#0D9488)
- Amber accents voor AI features (#F59E0B)
- Slate scale voor neutral colors
- WCAG AA compliant contrast

📦 Components:
- BuildTimeline (app/(marketing)/components)
- BentoGrid & BentoCard (components/ui)
- Button variants (components/ui)

📄 Content:
- timeline.json met volledige 4-weken data
- Features array per week
- Metrics en achievements tracking

🔧 Technical:
- Removed demo user scripts en migrations
- Updated middleware routes (removed /epd)
- TypeScript type fixes (hero-section-2, auth)
- Build succesvol: 13 routes generated

🚀 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
colinislit
2025-11-17 19:22:49 +01:00
parent 273c00f9e8
commit b7d19a8e1a
20 changed files with 2097 additions and 68 deletions

View File

@@ -0,0 +1,148 @@
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { Save, Loader2 } from 'lucide-react';
import type { ClientFormData } from '@/lib/types/client';
import type { Client } from '@/lib/types/client';
import { createClient, updateClient } from '../actions';
interface ClientFormProps {
client?: Client;
}
export function ClientForm({ client }: ClientFormProps) {
const router = useRouter();
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);
const [formData, setFormData] = useState<ClientFormData>({
first_name: client?.first_name || '',
last_name: client?.last_name || '',
birth_date: client?.birth_date || '',
});
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError(null);
setIsSubmitting(true);
try {
if (client) {
await updateClient(client.id, formData);
} else {
await createClient(formData);
}
router.push('/epd/clients');
router.refresh();
} catch (err) {
console.error('Form submission error:', err);
setError('Er is een fout opgetreden. Probeer het opnieuw.');
} finally {
setIsSubmitting(false);
}
};
const handleChange = (field: keyof ClientFormData, value: string) => {
setFormData((prev) => ({ ...prev, [field]: value }));
};
return (
<form onSubmit={handleSubmit} className="space-y-6">
{/* Error Message */}
{error && (
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
<p className="text-sm text-red-800">{error}</p>
</div>
)}
{/* Form Card */}
<div className="bg-white rounded-lg border border-slate-200 p-6 space-y-6">
{/* First Name */}
<div>
<label
htmlFor="first_name"
className="block text-sm font-medium text-slate-700 mb-2"
>
Voornaam <span className="text-red-500">*</span>
</label>
<input
type="text"
id="first_name"
required
value={formData.first_name}
onChange={(e) => handleChange('first_name', e.target.value)}
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent"
placeholder="bijv. Jan"
/>
</div>
{/* Last Name */}
<div>
<label
htmlFor="last_name"
className="block text-sm font-medium text-slate-700 mb-2"
>
Achternaam <span className="text-red-500">*</span>
</label>
<input
type="text"
id="last_name"
required
value={formData.last_name}
onChange={(e) => handleChange('last_name', e.target.value)}
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent"
placeholder="bijv. de Vries"
/>
</div>
{/* Birth Date */}
<div>
<label
htmlFor="birth_date"
className="block text-sm font-medium text-slate-700 mb-2"
>
Geboortedatum <span className="text-red-500">*</span>
</label>
<input
type="date"
id="birth_date"
required
value={formData.birth_date}
onChange={(e) => handleChange('birth_date', e.target.value)}
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent"
/>
</div>
</div>
{/* Form Actions */}
<div className="flex items-center justify-end gap-3">
<button
type="button"
onClick={() => router.back()}
disabled={isSubmitting}
className="px-6 py-2 border border-slate-300 text-slate-700 font-medium rounded-lg hover:bg-slate-50 transition-colors disabled:opacity-50"
>
Annuleren
</button>
<button
type="submit"
disabled={isSubmitting}
className="inline-flex items-center gap-2 px-6 py-2 bg-gradient-to-r from-teal-600 to-teal-700 hover:from-teal-700 hover:to-teal-800 text-white font-medium rounded-lg shadow-sm hover:shadow-md transition-all disabled:opacity-50"
>
{isSubmitting ? (
<>
<Loader2 className="h-4 w-4 animate-spin" />
<span>Bezig...</span>
</>
) : (
<>
<Save className="h-4 w-4" />
<span>{client ? 'Bijwerken' : 'Opslaan'}</span>
</>
)}
</button>
</div>
</form>
);
}