update deploy

This commit is contained in:
bulut
2026-03-27 10:41:54 +03:00
parent 69d19c0176
commit 6f6448aa06
422 changed files with 37956 additions and 0 deletions

View File

@@ -0,0 +1,629 @@
# 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 arama
- `sort` — 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):
```json
{
"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ıı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:
```json
{
"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
```tsx
// 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
```json
{
"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`
```json
{
"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:
```json
{
"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
```json
{
"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`
```json
{
"data": [
{
"id": 1,
"label": "Anasayfa",
"url": "/",
"location": "header",
"type": "link",
"parent_id": null,
"order_index": 0,
"is_active": true,
"children": []
}
]
}
```
---
## Duyurular
### GET /v1/announcements
```json
{
"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
```json
{
"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:
```json
{
"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ı:
```json
{
"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
```ts
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:
```ts
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 |