# Frontend (Next.js) — Lead Form Entegrasyonu ## API Endpoint ``` POST /api/v1/leads ``` Auth yok, public endpoint. Rate limited. --- ## Request Body ```json { "name": "Ad Soyad", "phone": "+90 532 724 15 32", "email": "ornek@email.com", "source": "kurs_kayit", "target_course": "gemici-birlesik-egitimi", "education_level": "lise", "subject": "Eğitim başvurusu", "message": "Bilgi almak istiyorum", "kvkk_consent": true, "marketing_consent": false, "utm_source": "google", "utm_medium": "cpc", "utm_campaign": "denizcilik-2026" } ``` ### Zorunlu Alanlar | Alan | Tip | Açıklama | |------|-----|----------| | `name` | string | Ad Soyad (max 255) | | `phone` | string | Telefon (max 20) | | `source` | string | Form kaynağı (aşağıya bak) | | `kvkk_consent` | boolean | **Zorunlu, `true` olmalı** — checkbox onaylatılmalı | ### Opsiyonel Alanlar | Alan | Tip | Açıklama | |------|-----|----------| | `email` | string | E-posta | | `target_course` | string | Hedef eğitim slug'ı | | `education_level` | string | Eğitim seviyesi | | `subject` | string | Konu | | `message` | string | Mesaj | | `marketing_consent` | boolean | Pazarlama onayı | | `utm_source` | string | Google Analytics UTM | | `utm_medium` | string | — | | `utm_campaign` | string | — | --- ## Source Değerleri (Form Bazlı) Her form kendi `source` değerini gönderir: | Form | source | Zorunlu Alanlar | Opsiyonel Alanlar | |------|--------|-----------------|-------------------| | Kurs ön kayıt | `kurs_kayit` | name, phone, kvkk_consent | email, target_course, education_level, message | | Danışmanlık | `danismanlik` | name, phone, kvkk_consent | email, message, target_course | | Duyuru sidebar | `duyuru` | name, phone, kvkk_consent | email | | İletişim | `iletisim` | name, phone, kvkk_consent | email, subject, message | | Hero form | `hero_form` | name, phone, kvkk_consent | target_course | | WhatsApp widget | `whatsapp_widget` | name, phone, kvkk_consent | — | --- ## Response ### Başarılı (201): ```json { "success": true, "message": "Talebiniz alınmıştır. En kısa sürede sizinle iletişime geçeceğiz." } ``` ### Validasyon Hatası (422): ```json { "message": "KVKK metnini onaylamanız gerekmektedir.", "errors": { "kvkk_consent": ["KVKK metnini onaylamanız gerekmektedir."], "name": ["Ad Soyad zorunludur."] } } ``` --- ## Form Örnekleri ### 1. Kurs Ön Kayıt Formu (Eğitim Detay Sayfası) ```tsx async function submitKursKayit(formData: FormData, courseSlug: string) { const utm = getUTMParams(); // URL'den utm_source, utm_medium, utm_campaign const res = await fetch(`${API_URL}/api/v1/leads`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: formData.get('name'), phone: formData.get('phone'), email: formData.get('email') || undefined, source: 'kurs_kayit', target_course: courseSlug, education_level: formData.get('education_level') || undefined, message: formData.get('message') || undefined, kvkk_consent: formData.get('kvkk') === 'on', marketing_consent: formData.get('marketing') === 'on', ...utm, }), }); if (!res.ok) { const err = await res.json(); throw new Error(err.message); } return res.json(); } ``` Form alanları: - Ad Soyad (zorunlu) - Telefon (zorunlu) - E-posta - Eğitim Seviyesi (select: ilkokul, ortaokul, lise, onlisans, lisans) - Mesaj - [x] KVKK metnini okudum ve onaylıyorum (zorunlu checkbox) - [ ] Kampanya ve duyurulardan haberdar olmak istiyorum ### 2. Danışmanlık Formu ```tsx body: JSON.stringify({ name: formData.get('name'), phone: formData.get('phone'), email: formData.get('email') || undefined, source: 'danismanlik', target_course: formData.get('interested_course') || undefined, message: formData.get('message') || undefined, kvkk_consent: true, ...utm, }) ``` ### 3. İletişim Formu ```tsx body: JSON.stringify({ name: formData.get('name'), phone: formData.get('phone'), email: formData.get('email') || undefined, source: 'iletisim', subject: formData.get('subject') || undefined, message: formData.get('message'), kvkk_consent: true, ...utm, }) ``` ### 4. Duyuru Sidebar Mini Form ```tsx body: JSON.stringify({ name: formData.get('name'), phone: formData.get('phone'), source: 'duyuru', kvkk_consent: true, ...utm, }) ``` ### 5. Hero Form (Anasayfa) ```tsx body: JSON.stringify({ name: formData.get('name'), phone: formData.get('phone'), source: 'hero_form', target_course: formData.get('course') || undefined, kvkk_consent: true, ...utm, }) ``` --- ## UTM Parametreleri URL'deki UTM parametrelerini otomatik olarak form submit'e ekle: ```tsx function getUTMParams(): Record { if (typeof window === 'undefined') return {}; const params = new URLSearchParams(window.location.search); const utm: Record = {}; for (const key of ['utm_source', 'utm_medium', 'utm_campaign']) { const val = params.get(key); if (val) utm[key] = val; } return utm; } ``` UTM değerleri sayfa boyunca korunmalı (cookie veya sessionStorage): ```tsx // Sayfa ilk yüklendiğinde useEffect(() => { const utm = getUTMParams(); if (Object.keys(utm).length > 0) { sessionStorage.setItem('utm', JSON.stringify(utm)); } }, []); // Form submit'te const utm = JSON.parse(sessionStorage.getItem('utm') || '{}'); ``` --- ## KVKK Checkbox Her formda KVKK onay checkbox'ı **zorunlu**. Backend `kvkk_consent: true` olmadan kabul etmez. ```tsx ``` --- ## Error Handling ```tsx try { const result = await submitLead(formData); // Başarılı — toast göster toast.success(result.message); form.reset(); } catch (error) { if (error.status === 422) { // Validasyon hataları — form alanlarının altında göster const { errors } = await error.json(); setFieldErrors(errors); } else if (error.status === 429) { // Rate limit — çok fazla istek toast.error('Çok fazla istek gönderildi. Lütfen biraz bekleyin.'); } else { toast.error('Bir hata oluştu. Lütfen tekrar deneyin.'); } } ``` --- ## TypeScript Tipi ```ts interface LeadFormData { name: string; phone: string; email?: string; source: 'kurs_kayit' | 'danismanlik' | 'duyuru' | 'iletisim' | 'hero_form' | 'whatsapp_widget'; target_course?: string; education_level?: string; subject?: string; message?: string; kvkk_consent: true; // Her zaman true olmalı marketing_consent?: boolean; utm_source?: string; utm_medium?: string; utm_campaign?: string; } interface LeadResponse { success: boolean; message: string; } ```