6.9 KiB
6.9 KiB
Frontend (Next.js) — Lead Form Entegrasyonu
API Endpoint
POST /api/v1/leads
Auth yok, public endpoint. Rate limited.
Request Body
{
"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 | |
| İ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):
{
"success": true,
"message": "Talebiniz alınmıştır. En kısa sürede sizinle iletişime geçeceğiz."
}
Validasyon Hatası (422):
{
"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ı)
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
- KVKK metnini okudum ve onaylıyorum (zorunlu checkbox)
- Kampanya ve duyurulardan haberdar olmak istiyorum
2. Danışmanlık Formu
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
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
body: JSON.stringify({
name: formData.get('name'),
phone: formData.get('phone'),
source: 'duyuru',
kvkk_consent: true,
...utm,
})
5. Hero Form (Anasayfa)
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:
function getUTMParams(): Record<string, string> {
if (typeof window === 'undefined') return {};
const params = new URLSearchParams(window.location.search);
const utm: Record<string, string> = {};
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):
// 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.
<label className="flex items-start gap-2">
<input type="checkbox" name="kvkk" required />
<span className="text-sm text-gray-600">
<a href="/kvkk" target="_blank" className="underline">
KVKK Aydınlatma Metni
</a>'ni okudum ve onaylıyorum.
</span>
</label>
Error Handling
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
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;
}