16 KiB
Frontend (Next.js) — API Entegrasyon Rehberi
Genel Bilgi
Backend: Laravel API, Base URL: {API_URL}/api/v1
Auth gerektirmeyen tüm public endpoint'ler burada. Frontend SSR/ISR ile fetch edebilir.
Tüm Public Endpoint'ler
| Method | Endpoint | Açıklama |
|---|---|---|
GET |
/v1/settings |
Site ayarları (nested group format) |
GET |
/v1/settings/{group} |
Tek grup ayarları |
GET |
/v1/categories |
Eğitim kategorileri |
GET |
/v1/categories/{slug} |
Kategori detayı |
GET |
/v1/courses |
Eğitim listesi (paginated) |
GET |
/v1/courses/{slug} |
Eğitim detayı (blocks + schedules dahil) |
GET |
/v1/schedules |
Eğitim takvimi |
GET |
/v1/schedules/upcoming |
Yaklaşan eğitimler |
GET |
/v1/schedules/{id} |
Takvim detayı |
GET |
/v1/announcements |
Duyurular |
GET |
/v1/announcements/{slug} |
Duyuru detayı |
GET |
/v1/hero-slides |
Hero slider (aktif, sıralı) |
GET |
/v1/faqs |
SSS listesi |
GET |
/v1/faqs/{category} |
Kategoriye göre SSS |
GET |
/v1/guide-cards |
Eğitim rehberi kartları |
GET |
/v1/menus/{location} |
Menü (header, footer, vb.) |
GET |
/v1/pages/{slug} |
Sayfa detayı (blocks dahil) |
GET |
/v1/preview/{token} |
Sayfa önizleme (cache bazlı) |
GET |
/v1/comments/{type}/{id} |
Yorumlar |
POST |
/v1/leads |
Lead/başvuru formu |
POST |
/v1/comments |
Yorum gönder |
GET |
/v1/sitemap-data |
Sitemap verisi |
Eğitimler (Courses)
GET /v1/courses — Liste
Query parametreleri:
category— Kategori slug filtresi (ör:guverte,stcw,makine)search— Başlık/açıklama aramasort— Sıralama:title,-created_at(varsayılan),students,rating(- prefix = desc)per_page— Sayfa başına (varsayılan: 15)
GET /v1/courses?category=guverte&sort=-rating&per_page=12
Response (paginated):
{
"data": [
{
"id": 1,
"category_id": 1,
"category": { "id": 1, "slug": "guverte", "label": "Güverte Eğitimleri" },
"slug": "gemici-birlesik-egitimi",
"title": "Gemici (Birleşik) Eğitimi",
"sub": "STCW / IMO Uyumlu",
"desc": "Güverte bölümünde gemici olarak görev yapmak isteyen...",
"long_desc": "Detaylı açıklama...",
"duration": "32 Gün",
"students": 772,
"rating": 4.9,
"badge": "most_preferred",
"badge_label": "En Çok Tercih Edilen",
"image": null,
"price": "₺14.500",
"includes": ["Basılı eğitim materyalleri", "Uygulamalı güverte tatbikatları", "..."],
"requirements": ["En az 16 yaşında olmak", "..."],
"scope": ["Denizde kişisel güvenlik", "Yangınla mücadele", "..."],
"standard": "STCW / IMO Uyumlu",
"language": "Türkçe",
"location": "Kadıköy, İstanbul",
"meta_title": "Gemici (Birleşik) Eğitimi | Boğaziçi Denizcilik",
"meta_description": "STCW A-II/4 uyumlu...",
"created_at": "2026-03-02T...",
"updated_at": "2026-03-23T..."
}
],
"links": { "first": "...", "last": "...", "prev": null, "next": "..." },
"meta": { "current_page": 1, "last_page": 3, "per_page": 12, "total": 32 }
}
GET /v1/courses/{slug} — Detay
Detayda ek olarak blocks ve schedules array'leri gelir:
{
"data": {
"id": 1,
"slug": "gemici-birlesik-egitimi",
"title": "Gemici (Birleşik) Eğitimi",
"sub": "STCW / IMO Uyumlu",
"desc": "...",
"long_desc": "...",
"duration": "32 Gün",
"students": 772,
"rating": 4.9,
"badge": "most_preferred",
"badge_label": "En Çok Tercih Edilen",
"image": null,
"price": "₺14.500",
"includes": ["Basılı eğitim materyalleri", "..."],
"requirements": ["En az 16 yaşında olmak", "..."],
"scope": ["Denizde kişisel güvenlik", "..."],
"standard": "STCW / IMO Uyumlu",
"language": "Türkçe",
"location": "Kadıköy, İstanbul",
"category": { "id": 1, "slug": "guverte", "label": "Güverte Eğitimleri" },
"blocks": [
{ "id": 1, "type": "text", "content": { "label": "EĞİTİM HAKKINDA", "title": "Neden Bu Eğitim?", "body": "<p>...</p>" }, "order_index": 0 },
{ "id": 2, "type": "text", "content": { "_width": "half", "label": "EĞİTİM KAPSAMI", "title": "Ne Öğreneceksiniz?", "body": "<ul>...</ul>" }, "order_index": 1 },
{ "id": 3, "type": "text", "content": { "_width": "half", "label": "BAŞVURU ŞARTLARI", "title": "Kimler Katılabilir?", "body": "<ul>...</ul>" }, "order_index": 2 },
{ "id": 4, "type": "stats_grid", "content": { "label": "EĞİTİM SÜRECİ", "title": "Başvurudan Belgeye 4 Adım", "stat_1_value": "01", "stat_1_label": "..." }, "order_index": 3 },
{ "id": 5, "type": "cards", "content": { "label": "KAZANIMLAR", "title": "...", "card_1_title": "...", "card_1_icon": "award" }, "order_index": 4 },
{ "id": 6, "type": "faq", "content": { "title": "...", "faq_1_question": "...", "faq_1_answer": "..." }, "order_index": 5 },
{ "id": 7, "type": "cta", "content": { "title": "...", "button_text": "Ön Kayıt Yap", "button_url": "/kayit?course=..." }, "order_index": 6 }
],
"schedules": [
{ "id": 1, "start_date": "2026-04-07", "end_date": "2026-05-08", "location": "Kadıköy", "quota": 20, "available_seats": 8, "is_urgent": false }
],
"meta_title": "...",
"meta_description": "..."
}
}
Badge Değerleri
| badge | badge_label | Kullanım |
|---|---|---|
most_preferred |
En Çok Tercih Edilen | Kart üstü etiket |
popular |
Popüler | Kart üstü etiket |
null |
— | Etiket yok |
Eğitim Blokları (Course Blocks)
Bloklar order_index sıralı gelir. Her bloğun type ve content JSON'ı var. Aynı renderer Page Blocks ile paylaşılabilir.
Blok Tipleri
| type | Açıklama | content key'leri |
|---|---|---|
text |
Zengin metin | label, title, body (HTML), _width |
cards |
Kart grid | label, title, card_N_title, card_N_text, card_N_icon |
stats_grid |
İstatistik/adım | label, title, stat_N_value, stat_N_label, style |
cta |
Call-to-action | title, description, button_text, button_url, button_2_text, button_2_url |
faq |
SSS | title, faq_N_question, faq_N_answer |
hero |
Hero banner | breadcrumb, title, highlight, subtitle, description |
text_image |
Metin + görsel | label, title, body, image, image_alt, image_position |
gallery |
Görsel galeri | Görsel listesi |
video |
Video embed | Video URL |
testimonials |
Referanslar | Yorum kartları |
html |
Serbest HTML | Ham HTML |
_width Sistemi
content._width blok genişliğini belirler:
"full"(varsayılan, key yoksa otomatik) — tam genişlik"half"— yarım genişlik, ardışık iki half blok yan yana render edilir
// Blok renderer örneği
function BlockRenderer({ blocks }) {
const grouped = groupConsecutiveHalfBlocks(blocks);
return grouped.map((item) => {
if (item.type === 'row') {
return (
<div className="grid grid-cols-2 gap-8">
{item.blocks.map(block => <Block key={block.id} {...block} />)}
</div>
);
}
return <Block key={item.id} {...item} />;
});
}
Kategoriler
GET /v1/categories
{
"data": [
{
"id": 1,
"slug": "guverte",
"label": "Güverte Eğitimleri",
"desc": "...",
"image": null,
"meta_title": "...",
"meta_description": "...",
"courses_count": 10,
"menu_courses": [
{ "title": "ARPA / Radar Simülatör Eğitimi", "slug": "arpa-radar-simulator" },
{ "title": "ECDIS Tip Bazlı Eğitim", "slug": "ecdis-tip-bazli-egitim" },
{ "title": "GMDSS Genel Telsiz Operatörü (GOC)", "slug": "gmdss-genel-telsiz-operatoru-goc" }
]
}
]
}
menu_courses: Her kategoriden menu_order 1-3 olan kurslar. Mega menu dropdown'unda kullanılır.
Kategori slug'ları: guverte, stcw, makine, yat-kaptanligi, yenileme, guvenlik
Sayfalar (Pages + Blocks)
GET /v1/pages/{slug}
Kurumsal sayfalar: kalite-politikasi, hakkimizda, vizyon-misyon
{
"data": {
"id": 1,
"slug": "kalite-politikasi",
"title": "Kalite Politikamız",
"meta_title": "...",
"meta_description": "...",
"is_active": true,
"blocks": [
{ "id": 1, "type": "hero", "content": { "breadcrumb": "...", "title": "...", "highlight": "..." }, "order_index": 0 },
{ "id": 2, "type": "cards", "content": { "label": "AKREDİTASYONLAR", "..." }, "order_index": 1 },
{ "id": 3, "type": "text", "content": { "_width": "half", "..." }, "order_index": 2 },
{ "id": 4, "type": "stats_grid", "content": { "_width": "half", "..." }, "order_index": 3 },
{ "id": 5, "type": "cta", "content": { "..." }, "order_index": 4 }
]
}
}
Page blocks ve course blocks aynı type/content yapısını kullanır. Tek bir BlockRenderer bileşeni her ikisi için de çalışır.
Hero Slides
GET /v1/hero-slides
Anasayfa slider:
{
"data": [
{
"id": 1,
"title": "Denizcilik Kariyerinize Başlayın",
"subtitle": "STCW uyumlu eğitim programları",
"button_text": "Eğitimleri İncele",
"button_url": "/egitimler",
"image": "/storage/uploads/hero-1.jpg",
"order_index": 0,
"is_active": true
}
]
}
Eğitim Takvimi (Schedules)
GET /v1/schedules
{
"data": [
{
"id": 1,
"course_id": 1,
"course": { "id": 1, "title": "Gemici (Birleşik) Eğitimi", "slug": "gemici-birlesik-egitimi", "..." },
"start_date": "2026-04-07",
"end_date": "2026-05-08",
"location": "Kadıköy",
"quota": 20,
"available_seats": 8,
"is_urgent": false
}
]
}
GET /v1/schedules/upcoming
Yaklaşan eğitimler (start_date >= today, sıralı).
Menüler
GET /v1/menus/{location}
Location değerleri: header, footer, sidebar
{
"data": [
{
"id": 1,
"label": "Anasayfa",
"url": "/",
"location": "header",
"type": "link",
"parent_id": null,
"order_index": 0,
"is_active": true,
"children": []
}
]
}
Duyurular
GET /v1/announcements
{
"data": [
{
"id": 1,
"slug": "2026-kayitlari-basladi",
"title": "2026 Kayıtları Başladı",
"category": "duyuru",
"excerpt": "...",
"content": "<p>HTML içerik...</p>",
"image": "/storage/uploads/...",
"is_featured": true,
"meta_title": "...",
"meta_description": "...",
"published_at": "2026-03-15"
}
]
}
SSS (FAQs)
GET /v1/faqs
{
"data": [
{
"id": 1,
"question": "STCW belgesi nedir?",
"answer": "...",
"category": "genel",
"order_index": 0,
"is_active": true
}
]
}
Kategoriye göre: GET /v1/faqs/genel
Lead Formu (Başvuru)
POST /v1/leads
Rate limited. Request:
{
"name": "Ad Soyad",
"phone": "+90 532 ...",
"source": "web",
"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"
}
Sayfa Önizleme (Preview)
GET /v1/preview/{token}
Admin panelden gönderilen önizleme. Auth yok, Next.js SSR ile fetch edilir. 10 dakika geçerli. Bulunamazsa 404.
Response formatı GET /v1/pages/{slug} ile aynı:
{
"data": {
"id": 1,
"slug": "kalite-politikasi",
"title": "...",
"blocks": [...]
}
}
Sayfa Bazlı Veri Haritası
Anasayfa (/)
| Veri | Endpoint |
|---|---|
| Hero slider | GET /v1/hero-slides |
| Eğitim kartları | GET /v1/courses?per_page=6&sort=-rating |
| Kategoriler | GET /v1/categories |
| Yaklaşan eğitimler | GET /v1/schedules/upcoming |
| SSS | GET /v1/faqs |
| Site ayarları | GET /v1/settings (layout'tan) |
Eğitimler (/egitimler)
| Veri | Endpoint |
|---|---|
| Eğitim listesi | GET /v1/courses?category=guverte&per_page=12 |
| Kategoriler (filter) | GET /v1/categories |
Eğitim Detay (/egitimler/{slug})
| Veri | Endpoint |
|---|---|
| Eğitim + bloklar + takvim | GET /v1/courses/{slug} |
Kurumsal Sayfalar (/kurumsal/{slug})
| Veri | Endpoint |
|---|---|
| Sayfa + bloklar | GET /v1/pages/{slug} |
Duyurular (/duyurular)
| Veri | Endpoint |
|---|---|
| Duyuru listesi | GET /v1/announcements |
İletişim (/iletisim)
| Veri | Endpoint |
|---|---|
| İletişim bilgileri | GET /v1/settings/contact |
| Harita | GET /v1/settings/maps |
| Form gönderimi | POST /v1/leads |
Layout (Her Sayfa)
| Veri | Endpoint |
|---|---|
| Header/Footer/SEO | GET /v1/settings |
| Menü | GET /v1/menus/header + GET /v1/menus/footer |
| Kategoriler (mega menu) | GET /v1/categories |
TypeScript Tipleri
interface Course {
id: number;
category_id: number;
category?: Category;
slug: string;
title: string;
sub: string | null;
desc: string;
long_desc: string;
duration: string;
students: number;
rating: number;
badge: 'most_preferred' | 'popular' | null;
badge_label: string | null;
image: string | null;
price: string;
includes: string[];
requirements: string[];
scope: string[];
standard: string | null;
language: string | null;
location: string | null;
blocks?: Block[];
schedules?: Schedule[];
meta_title: string | null;
meta_description: string | null;
created_at: string;
updated_at: string;
}
interface Category {
id: number;
slug: string;
label: string;
desc: string | null;
image: string | null;
courses_count?: number;
menu_courses?: { title: string; slug: string }[];
}
interface Block {
id: number;
type: 'hero' | 'text' | 'text_image' | 'cards' | 'stats_grid' | 'cta' | 'faq' | 'gallery' | 'video' | 'testimonials' | 'html';
content: Record<string, any>;
order_index: number;
}
interface Schedule {
id: number;
course_id: number;
course?: Course;
start_date: string;
end_date: string;
location: string;
quota: number;
available_seats: number;
is_urgent: boolean;
}
interface Page {
id: number;
slug: string;
title: string;
meta_title: string | null;
meta_description: string | null;
is_active: boolean;
blocks: Block[];
}
interface HeroSlide {
id: number;
title: string;
subtitle: string | null;
button_text: string | null;
button_url: string | null;
image: string | null;
order_index: number;
}
interface Announcement {
id: number;
slug: string;
title: string;
category: string;
excerpt: string | null;
content: string;
image: string | null;
is_featured: boolean;
published_at: string;
}
interface FAQ {
id: number;
question: string;
answer: string;
category: string;
order_index: number;
}
interface Lead {
name: string;
phone: string;
source?: string;
target_course?: string;
education_level?: string;
subject?: string;
message?: string;
kvkk_consent: boolean;
marketing_consent?: boolean;
utm_source?: string;
utm_medium?: string;
utm_campaign?: string;
}
interface MenuItem {
id: number;
label: string;
url: string;
location: string;
type: string;
parent_id: number | null;
order_index: number;
children: MenuItem[];
}
Image URL'leri
Image alanları relative path döner. API base URL ile birleştir:
const getImageUrl = (path: string | null): string | null => {
if (!path) return null;
if (path.startsWith('http')) return path;
return `${process.env.NEXT_PUBLIC_API_URL}${path}`;
};
Cache Stratejisi
| Veri | ISR revalidate |
|---|---|
| Settings | 300s (5 dk) |
| Categories | 300s |
| Courses list | 60s |
| Course detail | 60s |
| Pages | 300s |
| Hero slides | 300s |
| Schedules | 60s |
| Announcements | 120s |
| FAQs | 300s |
| Menus | 300s |