update deploy
This commit is contained in:
209
prompts/admin-course-blocks.md
Normal file
209
prompts/admin-course-blocks.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# Admin Panel — Eğitim Blokları (Course Blocks)
|
||||
|
||||
## Genel Bakış
|
||||
|
||||
Eğitim detay sayfalarında artık **Page Builder** mantığı var. Her eğitimin altına sıralı bloklar eklenebilir. Yapı, Page Blocks ile birebir aynı — aynı blok tipleri, aynı `_width` desteği, aynı content JSON formatı.
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
Tüm endpoint'ler `auth:sanctum` ile korunuyor. Base URL: `{API_URL}/api/admin`
|
||||
|
||||
| Method | Endpoint | Açıklama |
|
||||
|--------|----------|----------|
|
||||
| `GET` | `/admin/courses/{course}/blocks` | Eğitimin bloklarını listele |
|
||||
| `POST` | `/admin/courses/{course}/blocks` | Yeni blok oluştur |
|
||||
| `GET` | `/admin/courses/{course}/blocks/{block}` | Blok detayı |
|
||||
| `PUT` | `/admin/courses/{course}/blocks/{block}` | Blok güncelle |
|
||||
| `DELETE` | `/admin/courses/{course}/blocks/{block}` | Blok sil |
|
||||
| `POST` | `/admin/courses/{course}/blocks/reorder` | Sıralama güncelle |
|
||||
|
||||
---
|
||||
|
||||
## GET /admin/courses/{course}/blocks
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": "hero",
|
||||
"content": {
|
||||
"title": "Köprüüstü Kaynak Yönetimi",
|
||||
"subtitle": "BRM",
|
||||
"description": "STCW uyumlu ileri düzey eğitim..."
|
||||
},
|
||||
"order_index": 0
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "text",
|
||||
"content": {
|
||||
"_width": "half",
|
||||
"title": "Eğitim Kapsamı",
|
||||
"body": "<ul><li>Liderlik</li><li>İletişim</li></ul>"
|
||||
},
|
||||
"order_index": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## POST /admin/courses/{course}/blocks — Yeni Blok
|
||||
|
||||
### Request:
|
||||
```json
|
||||
{
|
||||
"type": "hero",
|
||||
"content": {
|
||||
"title": "Blok başlığı",
|
||||
"description": "Açıklama..."
|
||||
},
|
||||
"order_index": 0,
|
||||
"is_active": true
|
||||
}
|
||||
```
|
||||
|
||||
### Response (201):
|
||||
```json
|
||||
{
|
||||
"id": 5,
|
||||
"type": "hero",
|
||||
"content": { "title": "Blok başlığı", "description": "Açıklama..." },
|
||||
"order_index": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Validation Kuralları:
|
||||
| Alan | Kural |
|
||||
|------|-------|
|
||||
| `type` | required, string, max:50 |
|
||||
| `content` | present, array (boş obje `{}` gönderilebilir) |
|
||||
| `order_index` | optional, integer, min:0 (gönderilmezse otomatik son sıraya eklenir) |
|
||||
| `is_active` | optional, boolean |
|
||||
|
||||
---
|
||||
|
||||
## PUT /admin/courses/{course}/blocks/{block} — Güncelle
|
||||
|
||||
Sadece değişen alanları gönder:
|
||||
|
||||
```json
|
||||
{
|
||||
"content": {
|
||||
"_width": "half",
|
||||
"title": "Güncel Başlık",
|
||||
"body": "<p>Yeni içerik</p>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DELETE /admin/courses/{course}/blocks/{block}
|
||||
|
||||
```json
|
||||
{ "message": "Blok silindi." }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## POST /admin/courses/{course}/blocks/reorder — Sıralama
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{ "id": 3, "order_index": 0 },
|
||||
{ "id": 1, "order_index": 1 },
|
||||
{ "id": 2, "order_index": 2 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{ "message": "Blok sıralaması güncellendi." }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Blok Tipleri
|
||||
|
||||
Page Blocks ile aynı blok tipleri kullanılır:
|
||||
|
||||
| type | Açıklama | Örnek Kullanım |
|
||||
|------|----------|----------------|
|
||||
| `hero` | Üst banner/başlık alanı | Eğitim hero bölümü |
|
||||
| `text` | Zengin metin bloğu | Eğitim kapsamı, açıklama |
|
||||
| `text_image` | Metin + görsel yan yana | Eğitim tanıtımı |
|
||||
| `cards` | Kart grid | Özellikler, sertifikalar |
|
||||
| `stats_grid` | İstatistik/adım kartları | Süre, katılımcı, başarı oranı |
|
||||
| `cta` | Call-to-action | Kayıt ol butonu |
|
||||
| `faq` | Sıkça sorulan sorular | Eğitimle ilgili SSS |
|
||||
| `gallery` | Görsel galeri | Eğitim ortamı fotoğrafları |
|
||||
| `video` | Video embed | Tanıtım videosu |
|
||||
| `testimonials` | Yorumlar/referanslar | Mezun görüşleri |
|
||||
| `html` | Serbest HTML | Özel içerik |
|
||||
|
||||
---
|
||||
|
||||
## `_width` Desteği
|
||||
|
||||
`content` JSON içinde `_width` key'i blok genişliğini belirler:
|
||||
|
||||
| Değer | Açıklama |
|
||||
|-------|----------|
|
||||
| `"full"` | Tam genişlik (varsayılan — key gönderilmezse otomatik full) |
|
||||
| `"half"` | Yarım genişlik — ardışık iki half blok yan yana render edilir |
|
||||
|
||||
```json
|
||||
// Yan yana iki blok
|
||||
{ "type": "text", "content": { "_width": "half", "title": "Sol", "body": "..." }, "order_index": 0 }
|
||||
{ "type": "stats_grid", "content": { "_width": "half", "title": "Sağ", ... }, "order_index": 1 }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Public API — Frontend
|
||||
|
||||
`GET /api/v1/courses/{slug}` artık `blocks` array'ini de döner:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 1,
|
||||
"slug": "kopruustu-kaynak-yonetimi",
|
||||
"title": "Köprüüstü Kaynak Yönetimi (BRM)",
|
||||
"category": { ... },
|
||||
"blocks": [
|
||||
{ "id": 1, "type": "hero", "content": { ... }, "order_index": 0 },
|
||||
{ "id": 2, "type": "text", "content": { "_width": "half", ... }, "order_index": 1 },
|
||||
{ "id": 3, "type": "stats_grid", "content": { "_width": "half", ... }, "order_index": 2 }
|
||||
],
|
||||
"schedules": [ ... ],
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Frontend, sayfa blokları ile aynı `BlockRenderer` bileşenini kullanabilir.
|
||||
|
||||
---
|
||||
|
||||
## Admin Panel Entegrasyonu
|
||||
|
||||
### Önerilen UI:
|
||||
Eğitim düzenleme sayfasında (`/admin/courses/{id}/edit`) mevcut form alanlarının altına bir **"Bloklar"** sekmesi/bölümü ekle. Bu bölüm Page Builder ile aynı mantıkta çalışır:
|
||||
|
||||
1. Blok listesi `order_index` sıralı gösterilir
|
||||
2. Sürükle-bırak ile sıralama → `POST .../reorder`
|
||||
3. "Blok Ekle" butonu → tip seçimi → `POST .../blocks`
|
||||
4. Blok düzenleme → inline edit veya modal → `PUT .../blocks/{id}`
|
||||
5. Blok silme → onay dialog → `DELETE .../blocks/{id}`
|
||||
|
||||
### Aynı bileşenleri paylaşabilirsin:
|
||||
Page Blocks için yazdığın `BlockEditor`, `BlockTypeSelector`, `ContentEditor` bileşenlerini **doğrudan** course blocks için de kullan. Sadece API endpoint prefix'i değişir:
|
||||
- Page: `/admin/pages/{id}/blocks`
|
||||
- Course: `/admin/courses/{id}/blocks`
|
||||
246
prompts/admin-leads.md
Normal file
246
prompts/admin-leads.md
Normal file
@@ -0,0 +1,246 @@
|
||||
# Admin Panel — Leads (Başvuru Yönetimi) Modülü
|
||||
|
||||
## Genel Bakış
|
||||
|
||||
Web sitesindeki formlardan gelen tüm başvurular `leads` tablosuna yazılır. Admin panelden başvurular listelenir, detay görüntülenir, durum güncellenir ve not eklenir. **Lead'ler sadece API'den oluşur — admin panelde "Yeni Oluştur" butonu olmayacak.**
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints (Admin — auth:sanctum)
|
||||
|
||||
| Method | Endpoint | Açıklama |
|
||||
|--------|----------|----------|
|
||||
| `GET` | `/admin/leads` | Başvuruları listele (paginated, filtrelenebilir) |
|
||||
| `GET` | `/admin/leads/{id}` | Başvuru detayı (otomatik okundu işaretler) |
|
||||
| `PUT` | `/admin/leads/{id}` | Durum/not güncelle |
|
||||
| `DELETE` | `/admin/leads/{id}` | Başvuru sil (soft delete) |
|
||||
|
||||
---
|
||||
|
||||
## GET /admin/leads — Liste
|
||||
|
||||
### Query Parametreleri:
|
||||
| Parametre | Tip | Açıklama |
|
||||
|-----------|-----|----------|
|
||||
| `status` | string | Durum filtresi: `new`, `contacted`, `enrolled`, `cancelled` |
|
||||
| `source` | string | Kaynak filtresi: `kurs_kayit`, `danismanlik`, `duyuru`, `iletisim` |
|
||||
| `is_read` | boolean | Okundu/okunmadı filtresi |
|
||||
| `search` | string | İsim/telefon arama |
|
||||
| `per_page` | integer | Sayfa başına (varsayılan: 15) |
|
||||
|
||||
### Response:
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Ahmet Yılmaz",
|
||||
"phone": "+90 532 724 15 32",
|
||||
"email": "ahmet@email.com",
|
||||
"source": "kurs_kayit",
|
||||
"status": "new",
|
||||
"target_course": "gemici-birlesik-egitimi",
|
||||
"education_level": "lise",
|
||||
"subject": null,
|
||||
"message": "Eğitim hakkında bilgi almak istiyorum",
|
||||
"is_read": false,
|
||||
"kvkk_consent": true,
|
||||
"marketing_consent": false,
|
||||
"utm_source": "google",
|
||||
"utm_medium": "cpc",
|
||||
"utm_campaign": "denizcilik-2026",
|
||||
"admin_note": null,
|
||||
"created_at": "2026-03-24T10:30:00.000000Z",
|
||||
"updated_at": "2026-03-24T10:30:00.000000Z"
|
||||
}
|
||||
],
|
||||
"meta": { "current_page": 1, "last_page": 5, "per_page": 15, "total": 72 }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GET /admin/leads/{id} — Detay
|
||||
|
||||
Detay açıldığında backend otomatik olarak `is_read: true` yapar.
|
||||
|
||||
---
|
||||
|
||||
## PUT /admin/leads/{id} — Güncelle
|
||||
|
||||
Admin sadece şu alanları güncelleyebilir:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "contacted",
|
||||
"is_read": true,
|
||||
"admin_note": "Arandı, bilgi verildi. Nisan dönemine kayıt olacak."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Veri Modeli
|
||||
|
||||
### Lead Alanları
|
||||
|
||||
| Alan | Tip | Açıklama |
|
||||
|------|-----|----------|
|
||||
| `id` | integer | — |
|
||||
| `name` | string | Ad Soyad |
|
||||
| `phone` | string | Telefon |
|
||||
| `email` | string (nullable) | E-posta |
|
||||
| `source` | enum | Başvuru kaynağı (form tipi) |
|
||||
| `status` | enum | İşlem durumu |
|
||||
| `target_course` | string (nullable) | Hedef eğitim slug'ı |
|
||||
| `education_level` | string (nullable) | Eğitim seviyesi |
|
||||
| `subject` | string (nullable) | Konu (iletişim formunda) |
|
||||
| `message` | text (nullable) | Mesaj |
|
||||
| `is_read` | boolean | Admin tarafından okundu mu |
|
||||
| `kvkk_consent` | boolean | KVKK onayı |
|
||||
| `marketing_consent` | boolean | Pazarlama onayı |
|
||||
| `utm_source` | string (nullable) | UTM Source |
|
||||
| `utm_medium` | string (nullable) | UTM Medium |
|
||||
| `utm_campaign` | string (nullable) | UTM Campaign |
|
||||
| `admin_note` | text (nullable) | Admin notu |
|
||||
| `created_at` | datetime | Başvuru tarihi |
|
||||
|
||||
### Source (Kaynak) Değerleri
|
||||
|
||||
| Değer | Açıklama | Hangi Form? |
|
||||
|-------|----------|-------------|
|
||||
| `kurs_kayit` | Kurs ön kayıt | Eğitim detay sayfası kayıt formu |
|
||||
| `danismanlik` | Danışmanlık | Danışmanlık sayfası formu |
|
||||
| `duyuru` | Duyuru | Duyuru sidebar'daki mini form |
|
||||
| `iletisim` | İletişim | İletişim sayfası formu |
|
||||
| `hero_form` | Hero form | Anasayfa hero bölümü formu |
|
||||
| `whatsapp_widget` | WhatsApp | WhatsApp widget üzerinden gelen |
|
||||
|
||||
### Status (Durum) Değerleri
|
||||
|
||||
| Değer | Label | Badge Rengi | Açıklama |
|
||||
|-------|-------|-------------|----------|
|
||||
| `new` | Yeni | `warning` (sarı) | Henüz işlenmemiş |
|
||||
| `contacted` | İletişim Kuruldu | `info` (mavi) | Aranmış/e-posta gönderilmiş |
|
||||
| `enrolled` | Kayıt Oldu | `success` (yeşil) | Eğitime kaydolmuş |
|
||||
| `cancelled` | İptal | `danger` (kırmızı) | Vazgeçmiş/iptal |
|
||||
|
||||
---
|
||||
|
||||
## Önerilen Admin Panel Sayfası
|
||||
|
||||
### Liste Sayfası (`/admin/leads`)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Başvurular [Filtreler ▼] │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Filtreler: [Kaynak ▼] [Durum ▼] [Okundu ▼] [Ara...] │
|
||||
├──────┬──────────────┬──────────────┬────────────┬───────────┤
|
||||
│ ● │ Ahmet Yılmaz │ +90 532 ... │ kurs_kayit │ 🟡 Yeni │
|
||||
│ ○ │ Mehmet Kaya │ +90 555 ... │ iletisim │ 🔵 İlet. │
|
||||
│ ○ │ Ayşe Demir │ +90 212 ... │ danismanlik│ 🟢 Kayıt │
|
||||
└──────┴──────────────┴──────────────┴────────────┴───────────┘
|
||||
● = okunmadı (bold göster) ○ = okundu
|
||||
```
|
||||
|
||||
#### Tablo Sütunları:
|
||||
| Sütun | Açıklama |
|
||||
|-------|----------|
|
||||
| Okundu göstergesi | `is_read` false ise bold/dot göster |
|
||||
| Ad Soyad | `name` |
|
||||
| Telefon | `phone` |
|
||||
| E-posta | `email` (varsa) |
|
||||
| Kaynak | `source` — badge ile göster |
|
||||
| Hedef Eğitim | `target_course` (varsa) |
|
||||
| Durum | `status` — renkli badge |
|
||||
| Tarih | `created_at` — relative (2 saat önce) |
|
||||
|
||||
#### Kaynak Badge Renkleri:
|
||||
| Source | Renk |
|
||||
|--------|------|
|
||||
| `kurs_kayit` | `primary` (mavi) |
|
||||
| `danismanlik` | `purple` |
|
||||
| `duyuru` | `orange` |
|
||||
| `iletisim` | `teal` |
|
||||
| `hero_form` | `indigo` |
|
||||
| `whatsapp_widget` | `green` |
|
||||
|
||||
### Detay/Düzenleme Sayfası (`/admin/leads/{id}`)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Başvuru #42 — Ahmet Yılmaz │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ Bilgiler │
|
||||
│ ┌─────────────────┬───────────────────────────┐ │
|
||||
│ │ Ad Soyad │ Ahmet Yılmaz │ │
|
||||
│ │ Telefon │ +90 532 724 15 32 │ │
|
||||
│ │ E-posta │ ahmet@email.com │ │
|
||||
│ │ Kaynak │ 🔵 kurs_kayit │ │
|
||||
│ │ Hedef Eğitim │ Gemici (Birleşik) Eğitimi │ │
|
||||
│ │ Eğitim Seviyesi │ Lise │ │
|
||||
│ │ Mesaj │ Bilgi almak istiyorum │ │
|
||||
│ │ KVKK Onay │ ✅ Evet │ │
|
||||
│ │ Pazarlama Onay │ ❌ Hayır │ │
|
||||
│ │ Tarih │ 24 Mar 2026, 10:30 │ │
|
||||
│ └─────────────────┴───────────────────────────┘ │
|
||||
│ │
|
||||
│ UTM Bilgileri │
|
||||
│ ┌─────────────────┬───────────────────────────┐ │
|
||||
│ │ utm_source │ google │ │
|
||||
│ │ utm_medium │ cpc │ │
|
||||
│ │ utm_campaign │ denizcilik-2026 │ │
|
||||
│ └─────────────────┴───────────────────────────┘ │
|
||||
│ │
|
||||
│ İşlem │
|
||||
│ ┌───────────────────────────────────────────────┐│
|
||||
│ │ Durum: [Yeni ▼] → contacted / enrolled /.. ││
|
||||
│ │ ││
|
||||
│ │ Admin Notu: ││
|
||||
│ │ ┌─────────────────────────────────────────┐ ││
|
||||
│ │ │ Arandı, bilgi verildi. Nisan dönemine │ ││
|
||||
│ │ │ kayıt olacak. │ ││
|
||||
│ │ └─────────────────────────────────────────┘ ││
|
||||
│ │ ││
|
||||
│ │ [Kaydet] ││
|
||||
│ └───────────────────────────────────────────────┘│
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Düzenleme Kuralları:
|
||||
- Başvuru bilgileri (name, phone, message vb.) **readonly** — sadece görüntüleme
|
||||
- Sadece `status`, `is_read` ve `admin_note` düzenlenebilir
|
||||
- `target_course` slug ise, linke çevir: `/admin/courses/{slug}/edit`
|
||||
- "Yeni Oluştur" butonu **yok** — lead'ler sadece frontend formlarından gelir
|
||||
|
||||
---
|
||||
|
||||
## İstatistik Kartları (Dashboard için, opsiyonel)
|
||||
|
||||
Liste sayfasının üstüne istatistik kartları eklenebilir:
|
||||
|
||||
```
|
||||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ 🟡 Yeni │ │ 🔵 İletişim │ │ 🟢 Kayıt │ │ 📊 Toplam │
|
||||
│ 12 │ │ 28 │ │ 45 │ │ 93 │
|
||||
│ Bu hafta: 5 │ │ Bu hafta: 8 │ │ Bu ay: 15 │ │ Bu ay: 32 │
|
||||
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
API'den alınabilir:
|
||||
```
|
||||
GET /admin/leads?status=new → meta.total
|
||||
GET /admin/leads?status=contacted → meta.total
|
||||
GET /admin/leads?status=enrolled → meta.total
|
||||
GET /admin/leads → meta.total
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notlar
|
||||
|
||||
- `is_read` detay sayfası açıldığında otomatik `true` olur (backend tarafından)
|
||||
- Soft delete kullanılıyor — silinen lead'ler geri alınabilir
|
||||
- UTM verileri backend'de tek JSON sütununda (`utm`) saklanır, API response'da ayrı ayrı döner
|
||||
- KVKK ve pazarlama onayları readonly gösterilmeli — admin tarafından değiştirilemez
|
||||
375
prompts/admin-settings.md
Normal file
375
prompts/admin-settings.md
Normal file
@@ -0,0 +1,375 @@
|
||||
# Admin Panel — Site Ayarları Modülü
|
||||
|
||||
## Genel Bakış
|
||||
|
||||
Backend'de `settings` tablosu güncellendi. Artık her ayarın **label** (Türkçe etiket) ve **order_index** (sıralama) alanları var. Ayarlar **9 gruba** ayrılmış durumda. Admin panelde her grup kendi sekmesi/sayfası olacak.
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
Tüm admin endpoint'leri `auth:sanctum` ile korunuyor. Base URL: `{API_URL}/api/admin`
|
||||
|
||||
| Method | Endpoint | Açıklama |
|
||||
|--------|----------|----------|
|
||||
| `GET` | `/admin/settings` | Tüm ayarları flat liste olarak getir |
|
||||
| `GET` | `/admin/settings/group/{group}` | Tek grup ayarlarını getir |
|
||||
| `PUT` | `/admin/settings` | Toplu güncelleme (dot notation) |
|
||||
| `POST` | `/admin/settings/clear-cache` | Ayar cache'ini temizle |
|
||||
|
||||
---
|
||||
|
||||
## GET /admin/settings — Tüm Ayarlar
|
||||
|
||||
Her ayar şu formatta döner:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"key": "site_name",
|
||||
"value": "Boğaziçi Denizcilik Eğitim Kurumu",
|
||||
"group": "general",
|
||||
"type": "text",
|
||||
"label": "Site Adı",
|
||||
"order_index": 0
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"key": "announcement_bar_active",
|
||||
"value": "true",
|
||||
"group": "general",
|
||||
"type": "boolean",
|
||||
"label": "Üst Bar Aktif mi",
|
||||
"order_index": 7
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Frontend'de `group` bazında filtrele** → Her sekme/tab kendi grubunu gösterir.
|
||||
|
||||
---
|
||||
|
||||
## GET /admin/settings/group/{group} — Tek Grup
|
||||
|
||||
Sadece o grubun ayarlarını döner. `order_index` sıralı gelir.
|
||||
|
||||
Geçerli group değerleri:
|
||||
- `general` — Genel site ayarları
|
||||
- `contact` — İletişim bilgileri
|
||||
- `maps` — Harita ayarları
|
||||
- `social` — Sosyal medya linkleri
|
||||
- `seo` — SEO, Open Graph, Twitter Card, doğrulama kodları
|
||||
- `analytics` — Google Analytics, Tag Manager, Pixel ID'leri
|
||||
- `header` — Navbar ve üst bar ayarları
|
||||
- `footer` — Footer içerik ve stili
|
||||
- `integrations` — SMTP, reCAPTCHA, bildirim ayarları
|
||||
|
||||
---
|
||||
|
||||
## PUT /admin/settings — Toplu Güncelleme
|
||||
|
||||
**Dot notation** formatında gönder: `{group}.{key}: value`
|
||||
|
||||
### Request Body:
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": {
|
||||
"general.site_name": "Yeni Site Adı",
|
||||
"general.announcement_bar_active": "true",
|
||||
"contact.phone_primary": "+90 555 123 45 67",
|
||||
"social.instagram_url": "https://instagram.com/bogazicidenizcilik",
|
||||
"header.cta_button_text": "Kayıt Ol",
|
||||
"footer.copyright_text": "© 2026 Boğaziçi Denizcilik"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Response:
|
||||
```json
|
||||
{ "message": "Ayarlar güncellendi." }
|
||||
```
|
||||
|
||||
### Önemli:
|
||||
- Sadece değişen ayarları gönderin, hepsini göndermenize gerek yok
|
||||
- PUT sonrası backend otomatik olarak tüm cache'leri temizler
|
||||
- Validation: `settings` required array, her value nullable string
|
||||
|
||||
---
|
||||
|
||||
## POST /admin/settings/clear-cache
|
||||
|
||||
Body gerekmez. Manuel cache temizleme butonu için.
|
||||
|
||||
```json
|
||||
{ "message": "Ayar cache temizlendi." }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Type'lara Göre Form Bileşenleri
|
||||
|
||||
Her ayarın `type` alanı, admin panelde hangi input bileşeninin kullanılacağını belirler:
|
||||
|
||||
| type | Bileşen | Açıklama |
|
||||
|------|---------|----------|
|
||||
| `text` | `<input type="text">` | Tek satır metin |
|
||||
| `textarea` | `<textarea>` | Çok satır metin |
|
||||
| `image` | Dosya yükleme + önizleme | Mevcut upload endpoint'i kullanılır (`POST /admin/uploads`) |
|
||||
| `boolean` | Toggle/Switch | value `"true"` veya `"false"` string olarak saklanır |
|
||||
| `url` | `<input type="url">` | URL formatı |
|
||||
| `color` | Color picker | Hex renk kodu (#RRGGBB) |
|
||||
| `richtext` | TipTap / zengin metin editör | HTML içerik |
|
||||
| `json` | JSON editör | Yapısal veri |
|
||||
|
||||
### Boolean Değerler
|
||||
Backend'de `value` her zaman **string** olarak saklanır. Boolean'lar `"true"` / `"false"` string:
|
||||
```js
|
||||
// Gönderirken
|
||||
{ "settings": { "general.maintenance_mode": "false" } }
|
||||
|
||||
// Okurken
|
||||
setting.value === "true" // → toggle ON
|
||||
setting.value === "false" // → toggle OFF
|
||||
```
|
||||
|
||||
### Image Alanları
|
||||
1. Dosyayı `POST /admin/uploads` ile yükle → response'dan URL al
|
||||
2. URL'yi settings update'e gönder:
|
||||
```json
|
||||
{ "settings": { "general.logo_light": "/storage/uploads/logo-white.png" } }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Grup Detayları ve Alanlar
|
||||
|
||||
### general (13 alan)
|
||||
| key | type | label |
|
||||
|-----|------|-------|
|
||||
| site_name | text | Site Adı |
|
||||
| site_tagline | text | Slogan |
|
||||
| site_description | textarea | Kısa Site Açıklaması |
|
||||
| logo_light | image | Logo — Açık Tema (beyaz navbar) |
|
||||
| logo_dark | image | Logo — Koyu Tema (dark bg) |
|
||||
| favicon | image | Favicon (32x32 PNG) |
|
||||
| apple_touch_icon | image | Apple Touch Icon (180x180) |
|
||||
| announcement_bar_active | boolean | Üst Bar Aktif mi |
|
||||
| announcement_bar_text | text | Üst Bar Metni |
|
||||
| announcement_bar_url | url | Üst Bar Linki |
|
||||
| announcement_bar_bg_color | color | Üst Bar Arka Plan Rengi |
|
||||
| maintenance_mode | boolean | Bakım Modu |
|
||||
| maintenance_message | textarea | Bakım Modu Mesajı |
|
||||
|
||||
### contact (15 alan)
|
||||
| key | type | label |
|
||||
|-----|------|-------|
|
||||
| phone_primary | text | Ana Telefon |
|
||||
| phone_secondary | text | İkinci Telefon |
|
||||
| email_info | text | Bilgi E-postası |
|
||||
| email_support | text | Destek E-postası |
|
||||
| email_kayit | text | Kayıt E-postası |
|
||||
| address_full | textarea | Tam Adres |
|
||||
| address_short | text | Kısa Adres (navbar için) |
|
||||
| district | text | İlçe |
|
||||
| city | text | Şehir |
|
||||
| postal_code | text | Posta Kodu |
|
||||
| working_hours_weekday | text | Hafta İçi Saatleri |
|
||||
| working_hours_saturday | text | Cumartesi Saatleri |
|
||||
| working_hours_sunday | text | Pazar Saatleri |
|
||||
| whatsapp_number | text | WhatsApp (+90 ile başlayan) |
|
||||
| whatsapp_message | text | WhatsApp Varsayılan Mesaj |
|
||||
|
||||
### maps (6 alan)
|
||||
| key | type | label |
|
||||
|-----|------|-------|
|
||||
| google_maps_embed_url | textarea | Google Maps Embed URL (iframe src) |
|
||||
| google_maps_place_url | url | Google Maps Profil Linki |
|
||||
| google_maps_api_key | text | Google Maps API Key |
|
||||
| latitude | text | Enlem |
|
||||
| longitude | text | Boylam |
|
||||
| map_zoom_level | text | Harita Zoom (1-20) |
|
||||
|
||||
> **Not:** `google_maps_api_key` admin endpoint'ten görünür ama public API'den filtrelenir.
|
||||
|
||||
### social (11 alan)
|
||||
| key | type | label |
|
||||
|-----|------|-------|
|
||||
| instagram_url | url | Instagram Profil URL |
|
||||
| instagram_handle | text | Instagram Kullanıcı Adı (@siz) |
|
||||
| facebook_url | url | Facebook Sayfası URL |
|
||||
| facebook_page_id | text | Facebook Page ID |
|
||||
| twitter_url | url | X (Twitter) Profil URL |
|
||||
| twitter_handle | text | X Kullanıcı Adı (@siz) |
|
||||
| youtube_url | url | YouTube Kanal URL |
|
||||
| youtube_channel_id | text | YouTube Channel ID |
|
||||
| linkedin_url | url | LinkedIn Sayfa URL |
|
||||
| tiktok_url | url | TikTok Profil URL |
|
||||
| pinterest_url | url | Pinterest URL |
|
||||
|
||||
### seo (23 alan)
|
||||
|
||||
**Temel SEO:**
|
||||
| key | type | label |
|
||||
|-----|------|-------|
|
||||
| meta_title_suffix | text | Title Eki |
|
||||
| meta_title_separator | text | Ayraç Karakteri |
|
||||
| default_meta_description | textarea | Varsayılan Meta Açıklama |
|
||||
| default_meta_keywords | textarea | Varsayılan Keywords (virgülle) |
|
||||
| robots | text | Robots |
|
||||
| canonical_domain | url | Canonical Domain |
|
||||
|
||||
**Open Graph:**
|
||||
| key | type | label |
|
||||
|-----|------|-------|
|
||||
| og_title | text | OG Default Title |
|
||||
| og_description | textarea | OG Default Description |
|
||||
| og_image | image | OG Default Görsel (1200x630 px) |
|
||||
| og_type | text | OG Type |
|
||||
| og_locale | text | OG Locale |
|
||||
| og_site_name | text | OG Site Name |
|
||||
| facebook_app_id | text | Facebook App ID |
|
||||
|
||||
**Twitter / X Card:**
|
||||
| key | type | label |
|
||||
|-----|------|-------|
|
||||
| twitter_card_type | text | Card Tipi |
|
||||
| twitter_site | text | Site @handle |
|
||||
| twitter_creator | text | İçerik Sahibi @handle |
|
||||
| twitter_title | text | Twitter Default Title |
|
||||
| twitter_description | textarea | Twitter Default Description |
|
||||
| twitter_image | image | Twitter Card Görseli (1200x600 px) |
|
||||
|
||||
**Doğrulama Kodları:**
|
||||
| key | type | label |
|
||||
|-----|------|-------|
|
||||
| google_site_verification | text | Google Search Console Kodu |
|
||||
| bing_site_verification | text | Bing Webmaster Kodu |
|
||||
| yandex_verification | text | Yandex Webmaster Kodu |
|
||||
| pinterest_verification | text | Pinterest Doğrulama Kodu |
|
||||
|
||||
> **UI Önerisi:** SEO grubunu 4 alt sekmeye bölebilirsin: Temel SEO, Open Graph, Twitter Card, Doğrulama Kodları.
|
||||
|
||||
### analytics (10 alan)
|
||||
| key | type | label |
|
||||
|-----|------|-------|
|
||||
| google_analytics_id | text | Google Analytics 4 ID (G-XXXXXXXX) |
|
||||
| google_tag_manager_id | text | Google Tag Manager ID (GTM-XXXXXXX) |
|
||||
| google_ads_id | text | Google Ads Conversion ID |
|
||||
| facebook_pixel_id | text | Meta (Facebook) Pixel ID |
|
||||
| hotjar_id | text | Hotjar Site ID |
|
||||
| clarity_id | text | Microsoft Clarity ID |
|
||||
| tiktok_pixel_id | text | TikTok Pixel ID |
|
||||
| crisp_website_id | text | Crisp Chat Website ID |
|
||||
| custom_head_scripts | textarea | `<head>` içine özel script |
|
||||
| custom_body_scripts | textarea | `<body>` sonuna özel script |
|
||||
|
||||
### header (9 alan)
|
||||
| key | type | label |
|
||||
|-----|------|-------|
|
||||
| navbar_style_default | text | Varsayılan Navbar Stili (transparent/white) |
|
||||
| cta_button_text | text | Sağ Üst Buton Metni |
|
||||
| cta_button_url | url | Sağ Üst Buton Linki |
|
||||
| cta_button_color | color | Sağ Üst Buton Rengi |
|
||||
| show_phone_topbar | boolean | Üst Bar'da Telefon Göster |
|
||||
| show_email_topbar | boolean | Üst Bar'da E-posta Göster |
|
||||
| show_address_topbar | boolean | Üst Bar'da Adres Göster |
|
||||
| show_hours_topbar | boolean | Üst Bar'da Saat Göster |
|
||||
| show_social_navbar | boolean | Navbar'da Sosyal Medya İkonları Göster |
|
||||
|
||||
### footer (8 alan)
|
||||
| key | type | label |
|
||||
|-----|------|-------|
|
||||
| footer_description | textarea | Footer Açıklaması |
|
||||
| footer_logo | image | Footer Logo (varsa ayrı) |
|
||||
| copyright_text | text | Copyright Metni |
|
||||
| footer_address | textarea | Footer Adres |
|
||||
| footer_phone | text | Footer Telefon |
|
||||
| footer_email | text | Footer E-posta |
|
||||
| footer_bg_color | color | Footer Arka Plan Rengi |
|
||||
| show_social_footer | boolean | Footer'da Sosyal Medya Göster |
|
||||
|
||||
### integrations (10 alan)
|
||||
| key | type | label |
|
||||
|-----|------|-------|
|
||||
| recaptcha_site_key | text | reCAPTCHA v3 Site Key |
|
||||
| recaptcha_secret_key | text | reCAPTCHA v3 Secret Key |
|
||||
| smtp_host | text | SMTP Host |
|
||||
| smtp_port | text | SMTP Port |
|
||||
| smtp_username | text | SMTP Kullanıcı Adı |
|
||||
| smtp_password | text | SMTP Şifre |
|
||||
| smtp_encryption | text | SMTP Şifreleme (tls/ssl) |
|
||||
| smtp_from_name | text | Mail Gönderen Adı |
|
||||
| smtp_from_email | text | Mail Gönderen Adresi |
|
||||
| notification_emails | textarea | Bildirim E-postaları (virgülle) |
|
||||
|
||||
> **Not:** `integrations` grubundaki tüm ayarlar `is_public: false` — public API'den hiçbiri görünmez. `smtp_password` ve `*_secret_key` alanları password input olarak gösterilmeli.
|
||||
|
||||
---
|
||||
|
||||
## Önerilen Admin Panel Sayfa Yapısı
|
||||
|
||||
```
|
||||
/admin/settings
|
||||
├── Sidebar veya Tab Navigation
|
||||
│ ├── Genel → general
|
||||
│ ├── İletişim → contact
|
||||
│ ├── Harita → maps
|
||||
│ ├── Sosyal Medya → social
|
||||
│ ├── SEO → seo (alt sekmeler: Temel, OG, Twitter, Doğrulama)
|
||||
│ ├── Analitik → analytics
|
||||
│ ├── Header → header
|
||||
│ ├── Footer → footer
|
||||
│ └── Entegrasyonlar → integrations
|
||||
```
|
||||
|
||||
### Kaydet Akışı:
|
||||
1. Kullanıcı bir gruptaki alanları düzenler
|
||||
2. "Kaydet" butonuna basar
|
||||
3. Sadece **değişen** alanlar `PUT /admin/settings` ile gönderilir
|
||||
4. Başarılı response sonrası toast mesaj göster
|
||||
5. Gerekirse `POST /admin/settings/clear-cache` butonu ekle (teknik kullanıcılar için)
|
||||
|
||||
### Dinamik Form Render:
|
||||
`label` alanını form label olarak, `type` alanını input bileşeni seçmek için kullan. Backend'den gelen sıralama (`order_index`) form alanlarının sırası olarak kullanılabilir.
|
||||
|
||||
---
|
||||
|
||||
## Preview Sistemi (Page Builder)
|
||||
|
||||
Admin panelden sayfa blokları düzenlenirken önizleme özelliği:
|
||||
|
||||
| Method | Endpoint | Açıklama |
|
||||
|--------|----------|----------|
|
||||
| `POST` | `/admin/preview` | Önizleme token'ı oluştur |
|
||||
| `DELETE` | `/admin/preview/{token}` | Önizlemeyi sil |
|
||||
| `GET` | `/v1/preview/{token}` | Public — frontend fetch (auth yok) |
|
||||
|
||||
### POST /admin/preview
|
||||
|
||||
```json
|
||||
// Request
|
||||
{
|
||||
"page_id": 1,
|
||||
"blocks": [
|
||||
{ "type": "hero", "content": { "title": "...", "description": "..." }, "order_index": 0 },
|
||||
{ "type": "text", "content": { "_width": "half", "title": "...", "body": "..." }, "order_index": 1 }
|
||||
]
|
||||
}
|
||||
|
||||
// Response (201)
|
||||
{
|
||||
"token": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"preview_url": "http://localhost:3000/api/preview?token=550e8400...&slug=kalite-politikasi",
|
||||
"expires_in": 600
|
||||
}
|
||||
```
|
||||
|
||||
### Kullanım:
|
||||
1. Admin panelde "Önizle" butonuna bas → `POST /admin/preview`
|
||||
2. Response'daki `preview_url`'yi yeni sekmede aç
|
||||
3. Önizleme 10 dakika geçerli, sonra otomatik silinir
|
||||
4. İsteğe bağlı: modal kapatılınca `DELETE /admin/preview/{token}` ile temizle
|
||||
220
prompts/admin-stories-info-sections.md
Normal file
220
prompts/admin-stories-info-sections.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# Admin Panel — Stories + Info Sections
|
||||
|
||||
## 1. Stories (Hikayeler)
|
||||
|
||||
Anasayfadaki kayan hikaye/tanıtım kartları. Instagram stories benzeri yapı.
|
||||
|
||||
### API Endpoints
|
||||
|
||||
| Method | Endpoint | Auth | Açıklama |
|
||||
|--------|----------|------|----------|
|
||||
| `GET` | `/v1/stories` | public | Aktif hikayeler (order_index sıralı) |
|
||||
| `GET` | `/admin/stories` | sanctum | Tüm hikayeler |
|
||||
| `POST` | `/admin/stories` | sanctum | Yeni hikaye oluştur |
|
||||
| `GET` | `/admin/stories/{id}` | sanctum | Hikaye detayı |
|
||||
| `PUT` | `/admin/stories/{id}` | sanctum | Hikaye güncelle |
|
||||
| `DELETE` | `/admin/stories/{id}` | sanctum | Hikaye sil |
|
||||
|
||||
### Response Formatı
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "25 Yılı Aşkın Deneyim",
|
||||
"badge": "Tanıtım",
|
||||
"content": "1998'den bu yana 15.000+ denizci yetiştirdik...",
|
||||
"image": "/storage/stories/hakkimizda.jpg",
|
||||
"cta_text": "Hakkımızda",
|
||||
"cta_url": "/kurumsal/hakkimizda",
|
||||
"order_index": 0,
|
||||
"is_active": true,
|
||||
"created_at": "...",
|
||||
"updated_at": "..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Veri Modeli
|
||||
|
||||
| Alan | Tip | Açıklama |
|
||||
|------|-----|----------|
|
||||
| `title` | string (zorunlu) | Hikaye başlığı |
|
||||
| `badge` | string (nullable) | Üst etiket: "Tanıtım", "Yeni Dönem", vb. |
|
||||
| `content` | text (zorunlu) | Hikaye içeriği |
|
||||
| `image` | string (nullable) | Görsel path (storage/stories/) |
|
||||
| `cta_text` | string (nullable) | Buton metni: "Hakkımızda", "Detaylı Bilgi" |
|
||||
| `cta_url` | string (nullable) | Buton linki: "/kurumsal/hakkimizda" |
|
||||
| `order_index` | integer | Sıralama (0'dan başlar) |
|
||||
| `is_active` | boolean | Aktif/pasif |
|
||||
|
||||
### Admin Panel Form
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Hikaye Düzenle │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ Başlık: [25 Yılı Aşkın Deneyim ] │
|
||||
│ Etiket: [Tanıtım ] │
|
||||
│ İçerik: ┌─────────────────────────────────┐ │
|
||||
│ │ 1998'den bu yana 15.000+... │ │
|
||||
│ └─────────────────────────────────┘ │
|
||||
│ Görsel: [📷 Dosya Seç] hakkimizda.jpg │
|
||||
│ Buton: [Hakkımızda ] [/kurumsal/hak..] │
|
||||
│ Sıralama: [0] │
|
||||
│ Aktif: [✅ Toggle] │
|
||||
│ [Kaydet] │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Form Alanları:
|
||||
| Alan | Bileşen | Açıklama |
|
||||
|------|---------|----------|
|
||||
| title | TextInput | Zorunlu, max 255 |
|
||||
| badge | TextInput | İsteğe bağlı, max 100 |
|
||||
| content | Textarea | Zorunlu |
|
||||
| image | FileUpload | storage/public/stories, preview göster |
|
||||
| cta_text | TextInput | İsteğe bağlı, max 100 |
|
||||
| cta_url | TextInput (url) | İsteğe bağlı, max 255 |
|
||||
| order_index | NumberInput | Varsayılan 0 |
|
||||
| is_active | Toggle | Varsayılan true |
|
||||
|
||||
#### Tablo Sütunları:
|
||||
| Sütun | Açıklama |
|
||||
|-------|----------|
|
||||
| Görsel | Küçük thumbnail |
|
||||
| Başlık | title |
|
||||
| Etiket | badge (badge bileşeni) |
|
||||
| Buton | cta_text (varsa göster) |
|
||||
| Sıralama | order_index |
|
||||
| Aktif | is_active (toggle/icon) |
|
||||
|
||||
---
|
||||
|
||||
## 2. Info Sections (Tanıtım Bölümleri)
|
||||
|
||||
Anasayfadaki 2 adet metin+görsel tanıtım bölümü. Settings tablosunda `info_sections` grubu altında saklanır.
|
||||
|
||||
### API Erişimi
|
||||
|
||||
Mevcut settings endpoint'inden gelir:
|
||||
|
||||
```
|
||||
GET /api/v1/settings
|
||||
GET /api/v1/settings/info_sections
|
||||
```
|
||||
|
||||
### Response (settings/info_sections):
|
||||
|
||||
```json
|
||||
{
|
||||
"info_section_1_badge": "Neden Boğaziçi Denizcilik?",
|
||||
"info_section_1_title": "Uluslararası Standartlarda Eğitim",
|
||||
"info_section_1_body": "Uzun açıklama metni...",
|
||||
"info_section_1_quote": "Denizcilik eğitiminde kalite...",
|
||||
"info_section_1_quote_author": "Kpt. Murat Aydın, Kurucu",
|
||||
"info_section_1_image": "/storage/uploads/info-1.jpg",
|
||||
"info_section_2_badge": "Simülatör Destekli Eğitim",
|
||||
"info_section_2_title": "Teoriden Pratiğe",
|
||||
"info_section_2_body": "Uzun açıklama metni...",
|
||||
"info_section_2_image": "/storage/uploads/info-2.jpg"
|
||||
}
|
||||
```
|
||||
|
||||
### Alanlar
|
||||
|
||||
**Bölüm 1 (6 alan):**
|
||||
| Key | Type | Label |
|
||||
|-----|------|-------|
|
||||
| `info_section_1_badge` | text | Bölüm 1 — Etiket |
|
||||
| `info_section_1_title` | text | Bölüm 1 — Başlık |
|
||||
| `info_section_1_body` | textarea | Bölüm 1 — İçerik |
|
||||
| `info_section_1_quote` | text | Bölüm 1 — Alıntı |
|
||||
| `info_section_1_quote_author` | text | Bölüm 1 — Alıntı Yazarı |
|
||||
| `info_section_1_image` | image | Bölüm 1 — Görsel |
|
||||
|
||||
**Bölüm 2 (4 alan):**
|
||||
| Key | Type | Label |
|
||||
|-----|------|-------|
|
||||
| `info_section_2_badge` | text | Bölüm 2 — Etiket |
|
||||
| `info_section_2_title` | text | Bölüm 2 — Başlık |
|
||||
| `info_section_2_body` | textarea | Bölüm 2 — İçerik |
|
||||
| `info_section_2_image` | image | Bölüm 2 — Görsel |
|
||||
|
||||
### Admin Panel — Ayarlar Sayfası
|
||||
|
||||
Mevcut Settings sayfasına yeni bir **"Tanıtım Bölümleri"** sekmesi ekle:
|
||||
|
||||
```
|
||||
/admin/settings
|
||||
├── ... (mevcut sekmeler)
|
||||
└── Tanıtım Bölümleri → info_sections
|
||||
├── Bölüm 1
|
||||
│ ├── Etiket: [Neden Boğaziçi Denizcilik?]
|
||||
│ ├── Başlık: [Uluslararası Standartlarda...]
|
||||
│ ├── İçerik: [textarea — uzun metin]
|
||||
│ ├── Alıntı: [Denizcilik eğitiminde kalite...]
|
||||
│ ├── Alıntı Yazarı: [Kpt. Murat Aydın, Kurucu]
|
||||
│ └── Görsel: [📷 FileUpload]
|
||||
└── Bölüm 2
|
||||
├── Etiket: [Simülatör Destekli Eğitim]
|
||||
├── Başlık: [Teoriden Pratiğe]
|
||||
├── İçerik: [textarea — uzun metin]
|
||||
└── Görsel: [📷 FileUpload]
|
||||
```
|
||||
|
||||
Kaydet: `PUT /admin/settings` ile dot notation:
|
||||
```json
|
||||
{
|
||||
"settings": {
|
||||
"info_sections.info_section_1_badge": "Neden Boğaziçi Denizcilik?",
|
||||
"info_sections.info_section_1_title": "Uluslararası Standartlarda Eğitim",
|
||||
"info_sections.info_section_1_body": "...",
|
||||
"info_sections.info_section_1_image": "/storage/uploads/info-1.jpg"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Frontend Kullanım Özeti
|
||||
|
||||
### Stories
|
||||
```tsx
|
||||
// Anasayfa
|
||||
const { data: stories } = await fetch('/api/v1/stories');
|
||||
|
||||
{stories.map(story => (
|
||||
<StoryCard
|
||||
key={story.id}
|
||||
title={story.title}
|
||||
badge={story.badge}
|
||||
content={story.content}
|
||||
image={getImageUrl(story.image)}
|
||||
ctaText={story.cta_text}
|
||||
ctaUrl={story.cta_url}
|
||||
/>
|
||||
))}
|
||||
```
|
||||
|
||||
### Info Sections
|
||||
```tsx
|
||||
// Settings'ten al
|
||||
const settings = await fetch('/api/v1/settings');
|
||||
const info1 = {
|
||||
badge: settings.info_sections?.info_section_1_badge,
|
||||
title: settings.info_sections?.info_section_1_title,
|
||||
body: settings.info_sections?.info_section_1_body,
|
||||
quote: settings.info_sections?.info_section_1_quote,
|
||||
quoteAuthor: settings.info_sections?.info_section_1_quote_author,
|
||||
image: settings.info_sections?.info_section_1_image,
|
||||
};
|
||||
|
||||
// Bölüm 1 — görsel solda, metin sağda
|
||||
<InfoSection {...info1} imagePosition="left" />
|
||||
|
||||
// Bölüm 2 — görsel sağda, metin solda (reverse)
|
||||
<InfoSection {...info2} imagePosition="right" />
|
||||
```
|
||||
629
prompts/frontend-api-integration.md
Normal file
629
prompts/frontend-api-integration.md
Normal 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ı 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:
|
||||
|
||||
```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 |
|
||||
293
prompts/frontend-leads.md
Normal file
293
prompts/frontend-leads.md
Normal file
@@ -0,0 +1,293 @@
|
||||
# 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<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):
|
||||
|
||||
```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
|
||||
<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
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
380
prompts/frontend-settings.md
Normal file
380
prompts/frontend-settings.md
Normal file
@@ -0,0 +1,380 @@
|
||||
# Frontend (Next.js) — Site Ayarları Entegrasyonu
|
||||
|
||||
## Genel Bakış
|
||||
|
||||
Backend'den tüm site ayarları **tek bir API çağrısı** ile alınır. Ayarlar gruplara ayrılmış nested JSON formatında gelir. Bu veri layout, header, footer, SEO meta tag'leri, analytics scriptleri ve tüm sabit içerikleri besler.
|
||||
|
||||
**Sensitive key'ler (API key, secret, password) public endpoint'ten filtrelenmiştir — güvenle kullanabilirsin.**
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints (Public — Auth Yok)
|
||||
|
||||
| Method | Endpoint | Açıklama |
|
||||
|--------|----------|----------|
|
||||
| `GET` | `/api/v1/settings` | Tüm public ayarlar (grouped nested JSON) |
|
||||
| `GET` | `/api/v1/settings/{group}` | Tek grup ayarları (flat key-value) |
|
||||
|
||||
---
|
||||
|
||||
## GET /api/v1/settings — Response Formatı
|
||||
|
||||
```json
|
||||
{
|
||||
"general": {
|
||||
"site_name": "Boğaziçi Denizcilik Eğitim Kurumu",
|
||||
"site_tagline": "Türkiye'nin Köklü Denizcilik Okulu",
|
||||
"site_description": "Türkiye'nin köklü denizcilik eğitim kurumu",
|
||||
"logo_light": "/storage/uploads/logo-white.png",
|
||||
"logo_dark": "/storage/uploads/logo-dark.png",
|
||||
"favicon": "/storage/uploads/favicon.png",
|
||||
"apple_touch_icon": null,
|
||||
"announcement_bar_active": "true",
|
||||
"announcement_bar_text": "2026 Kayıtları Devam Ediyor",
|
||||
"announcement_bar_url": "/kayit",
|
||||
"announcement_bar_bg_color": "#1a3e74",
|
||||
"maintenance_mode": "false",
|
||||
"maintenance_message": "Sitemiz bakımdadır, kısa süre içinde geri döneceğiz."
|
||||
},
|
||||
"contact": {
|
||||
"phone_primary": "+90 532 724 15 32",
|
||||
"phone_secondary": null,
|
||||
"email_info": "bilgi@bogazicidenizcilik.com",
|
||||
"address_full": "Osmanağa Mah. Çuhadarağa Sk. No:21 Kadıköy/İstanbul",
|
||||
"address_short": "Kadıköy, İstanbul",
|
||||
"working_hours_weekday": "Hafta İçi 09:00 – 17:00",
|
||||
"whatsapp_number": null,
|
||||
"whatsapp_message": "Merhaba, bilgi almak istiyorum."
|
||||
},
|
||||
"maps": {
|
||||
"google_maps_embed_url": null,
|
||||
"google_maps_place_url": null,
|
||||
"latitude": "40.9876",
|
||||
"longitude": "29.0234",
|
||||
"map_zoom_level": "15"
|
||||
},
|
||||
"social": {
|
||||
"instagram_url": null,
|
||||
"instagram_handle": null,
|
||||
"facebook_url": null,
|
||||
"youtube_url": null,
|
||||
"linkedin_url": null,
|
||||
"tiktok_url": null,
|
||||
"twitter_url": null
|
||||
},
|
||||
"seo": {
|
||||
"meta_title_suffix": "| Boğaziçi Denizcilik",
|
||||
"meta_title_separator": "|",
|
||||
"default_meta_description": "IMO ve STCW standartlarında denizcilik eğitimi.",
|
||||
"default_meta_keywords": "denizcilik kursu, STCW eğitimi, kaptan kursu",
|
||||
"robots": "index, follow",
|
||||
"canonical_domain": "https://bogazicidenizcilik.com",
|
||||
"og_image": null,
|
||||
"og_type": "website",
|
||||
"og_locale": "tr_TR",
|
||||
"og_site_name": "Boğaziçi Denizcilik",
|
||||
"twitter_card_type": "summary_large_image",
|
||||
"google_site_verification": null
|
||||
},
|
||||
"analytics": {
|
||||
"google_analytics_id": null,
|
||||
"google_tag_manager_id": null,
|
||||
"facebook_pixel_id": null,
|
||||
"hotjar_id": null,
|
||||
"clarity_id": null,
|
||||
"crisp_website_id": null,
|
||||
"custom_head_scripts": null,
|
||||
"custom_body_scripts": null
|
||||
},
|
||||
"header": {
|
||||
"navbar_style_default": "transparent",
|
||||
"cta_button_text": "Başvuru Yap",
|
||||
"cta_button_url": "/kayit",
|
||||
"cta_button_color": "#396cab",
|
||||
"show_phone_topbar": "true",
|
||||
"show_email_topbar": "true",
|
||||
"show_address_topbar": "true",
|
||||
"show_hours_topbar": "true",
|
||||
"show_social_navbar": "true"
|
||||
},
|
||||
"footer": {
|
||||
"footer_description": "Türkiye'nin köklü denizcilik eğitim kurumlarından biri olarak...",
|
||||
"footer_logo": null,
|
||||
"copyright_text": "© 2026 Boğaziçi Denizcilik Eğitim Kurumu. Tüm hakları saklıdır.",
|
||||
"footer_address": "Osmanağa Bahariye Cad. No:31 Kadıköy/İstanbul",
|
||||
"footer_phone": "+90 532 724 15 32",
|
||||
"footer_email": "bilgi@bogazicidenizcilik.com",
|
||||
"footer_bg_color": "#0f2447",
|
||||
"show_social_footer": "true"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Not:** `integrations` grubu public API'den dönmez (tamamı `is_public: false`). `maps.google_maps_api_key` de filtrelenmiştir.
|
||||
|
||||
---
|
||||
|
||||
## GET /api/v1/settings/{group} — Tek Grup
|
||||
|
||||
```
|
||||
GET /api/v1/settings/contact
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"phone_primary": "+90 532 724 15 32",
|
||||
"email_info": "bilgi@bogazicidenizcilik.com",
|
||||
"address_full": "Osmanağa Mah. Çuhadarağa Sk. No:21 Kadıköy/İstanbul",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Geçerli group değerleri: `general`, `contact`, `maps`, `social`, `seo`, `analytics`, `header`, `footer`
|
||||
|
||||
---
|
||||
|
||||
## Önerilen Veri Akışı (Next.js)
|
||||
|
||||
### 1. Layout-Level Fetch (Server Component)
|
||||
|
||||
Ayarlar tüm sayfalarda gerekli (header, footer, SEO). Layout'ta bir kez fetch edip context/provider ile dağıt:
|
||||
|
||||
```tsx
|
||||
// app/layout.tsx
|
||||
async function getSettings() {
|
||||
const res = await fetch(`${process.env.API_URL}/api/v1/settings`, {
|
||||
next: { revalidate: 300 } // 5 dk cache
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export default async function RootLayout({ children }) {
|
||||
const settings = await getSettings();
|
||||
|
||||
return (
|
||||
<html>
|
||||
<head>
|
||||
{/* Analytics Scripts */}
|
||||
{settings.analytics?.google_tag_manager_id && (
|
||||
<script>...</script>
|
||||
)}
|
||||
{settings.analytics?.custom_head_scripts && (
|
||||
<div dangerouslySetInnerHTML={{ __html: settings.analytics.custom_head_scripts }} />
|
||||
)}
|
||||
</head>
|
||||
<body>
|
||||
<SettingsProvider value={settings}>
|
||||
<Header settings={settings} />
|
||||
{children}
|
||||
<Footer settings={settings} />
|
||||
</SettingsProvider>
|
||||
{settings.analytics?.custom_body_scripts && (
|
||||
<div dangerouslySetInnerHTML={{ __html: settings.analytics.custom_body_scripts }} />
|
||||
)}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Boolean Değerler
|
||||
|
||||
Backend tüm değerleri **string** olarak döner. Boolean kontrol:
|
||||
|
||||
```tsx
|
||||
// Helper fonksiyon
|
||||
const isEnabled = (value: string | null) => value === "true";
|
||||
|
||||
// Kullanım
|
||||
{isEnabled(settings.general.announcement_bar_active) && (
|
||||
<AnnouncementBar
|
||||
text={settings.general.announcement_bar_text}
|
||||
url={settings.general.announcement_bar_url}
|
||||
bgColor={settings.general.announcement_bar_bg_color}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isEnabled(settings.header.show_phone_topbar) && (
|
||||
<span>{settings.contact.phone_primary}</span>
|
||||
)}
|
||||
```
|
||||
|
||||
### 3. Image Alanları
|
||||
|
||||
Image değerleri relative path olarak gelir. API base URL ile birleştir:
|
||||
|
||||
```tsx
|
||||
const getImageUrl = (path: string | null) => {
|
||||
if (!path) return null;
|
||||
if (path.startsWith('http')) return path;
|
||||
return `${process.env.NEXT_PUBLIC_API_URL}${path}`;
|
||||
};
|
||||
|
||||
// Kullanım
|
||||
<Image src={getImageUrl(settings.general.logo_light)} alt={settings.general.site_name} />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bileşen Bazlı Kullanım Haritası
|
||||
|
||||
### Header / Navbar
|
||||
| Ayar | Kullanım |
|
||||
|------|----------|
|
||||
| `general.logo_light` | Navbar logo (transparent bg) |
|
||||
| `general.logo_dark` | Navbar logo (scrolled/white bg) |
|
||||
| `general.announcement_bar_*` | Üst duyuru barı |
|
||||
| `header.navbar_style_default` | Navbar başlangıç stili |
|
||||
| `header.cta_button_text/url/color` | Sağ üst CTA butonu |
|
||||
| `header.show_*_topbar` | Topbar'da göster/gizle kontrolleri |
|
||||
| `header.show_social_navbar` | Sosyal medya ikonları |
|
||||
| `contact.phone_primary` | Topbar telefon |
|
||||
| `contact.email_info` | Topbar e-posta |
|
||||
| `contact.address_short` | Topbar adres |
|
||||
| `contact.working_hours_weekday` | Topbar çalışma saatleri |
|
||||
| `social.*_url` | Sosyal medya ikon linkleri |
|
||||
|
||||
### Footer
|
||||
| Ayar | Kullanım |
|
||||
|------|----------|
|
||||
| `footer.footer_logo` | Footer logo |
|
||||
| `footer.footer_description` | Footer açıklama metni |
|
||||
| `footer.footer_address` | Adres |
|
||||
| `footer.footer_phone` | Telefon |
|
||||
| `footer.footer_email` | E-posta |
|
||||
| `footer.copyright_text` | Copyright satırı |
|
||||
| `footer.footer_bg_color` | Background rengi |
|
||||
| `footer.show_social_footer` | Sosyal medya göster/gizle |
|
||||
| `social.*_url` | Sosyal medya ikon linkleri |
|
||||
|
||||
### SEO / Head Meta Tags
|
||||
```tsx
|
||||
// Her sayfa için generateMetadata
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const settings = await getSettings();
|
||||
const seo = settings.seo;
|
||||
|
||||
return {
|
||||
title: {
|
||||
template: `%s ${seo.meta_title_separator} ${seo.og_site_name}`,
|
||||
default: settings.general.site_name,
|
||||
},
|
||||
description: seo.default_meta_description,
|
||||
keywords: seo.default_meta_keywords,
|
||||
robots: seo.robots,
|
||||
openGraph: {
|
||||
type: seo.og_type,
|
||||
locale: seo.og_locale,
|
||||
siteName: seo.og_site_name,
|
||||
images: seo.og_image ? [getImageUrl(seo.og_image)] : [],
|
||||
},
|
||||
twitter: {
|
||||
card: seo.twitter_card_type,
|
||||
site: seo.twitter_site,
|
||||
creator: seo.twitter_creator,
|
||||
},
|
||||
verification: {
|
||||
google: seo.google_site_verification,
|
||||
other: {
|
||||
'yandex-verification': seo.yandex_verification,
|
||||
'msvalidate.01': seo.bing_site_verification,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### İletişim Sayfası
|
||||
| Ayar | Kullanım |
|
||||
|------|----------|
|
||||
| `contact.phone_primary/secondary` | Telefon numaraları |
|
||||
| `contact.email_*` | E-posta adresleri |
|
||||
| `contact.address_full` | Tam adres |
|
||||
| `contact.working_hours_*` | Çalışma saatleri |
|
||||
| `contact.whatsapp_number/message` | WhatsApp butonu |
|
||||
| `maps.google_maps_embed_url` | Harita iframe |
|
||||
| `maps.google_maps_place_url` | "Google Maps'te Aç" linki |
|
||||
| `maps.latitude/longitude` | Marker konumu |
|
||||
|
||||
### WhatsApp Floating Button
|
||||
```tsx
|
||||
const whatsappUrl = settings.contact.whatsapp_number
|
||||
? `https://wa.me/${settings.contact.whatsapp_number.replace(/\s/g, '')}?text=${encodeURIComponent(settings.contact.whatsapp_message || '')}`
|
||||
: null;
|
||||
|
||||
{whatsappUrl && <a href={whatsappUrl} target="_blank">WhatsApp</a>}
|
||||
```
|
||||
|
||||
### Bakım Modu
|
||||
```tsx
|
||||
// middleware.ts veya layout.tsx
|
||||
if (isEnabled(settings.general.maintenance_mode)) {
|
||||
return <MaintenancePage message={settings.general.maintenance_message} />;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cache Stratejisi
|
||||
|
||||
| Strateji | Açıklama |
|
||||
|----------|----------|
|
||||
| Backend | 1 saat cache (`Cache::remember`, 3600s) |
|
||||
| Frontend (ISR) | `next: { revalidate: 300 }` — 5 dakika |
|
||||
| Admin güncelleme sonrası | Backend cache otomatik temizlenir, frontend 5 dk içinde güncellenir |
|
||||
| Acil güncelleme | Admin panelden "Cache Temizle" → frontend revalidate |
|
||||
|
||||
---
|
||||
|
||||
## Null Değer Kontrolü
|
||||
|
||||
Birçok ayar başlangıçta `null` olabilir. Her kullanımda null check yap:
|
||||
|
||||
```tsx
|
||||
// Kötü
|
||||
<a href={settings.social.instagram_url}>Instagram</a>
|
||||
|
||||
// İyi
|
||||
{settings.social?.instagram_url && (
|
||||
<a href={settings.social.instagram_url}>Instagram</a>
|
||||
)}
|
||||
```
|
||||
|
||||
Sosyal medya ikonlarını dinamik render et — sadece URL'si dolu olanları göster:
|
||||
|
||||
```tsx
|
||||
const socialLinks = [
|
||||
{ key: 'instagram_url', icon: Instagram, label: 'Instagram' },
|
||||
{ key: 'facebook_url', icon: Facebook, label: 'Facebook' },
|
||||
{ key: 'twitter_url', icon: Twitter, label: 'X' },
|
||||
{ key: 'youtube_url', icon: Youtube, label: 'YouTube' },
|
||||
{ key: 'linkedin_url', icon: Linkedin, label: 'LinkedIn' },
|
||||
{ key: 'tiktok_url', icon: TikTok, label: 'TikTok' },
|
||||
];
|
||||
|
||||
{socialLinks
|
||||
.filter(s => settings.social?.[s.key])
|
||||
.map(s => (
|
||||
<a key={s.key} href={settings.social[s.key]} target="_blank" aria-label={s.label}>
|
||||
<s.icon />
|
||||
</a>
|
||||
))
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## TypeScript Tipi (Önerilen)
|
||||
|
||||
```ts
|
||||
interface SiteSettings {
|
||||
general: Record<string, string | null>;
|
||||
contact: Record<string, string | null>;
|
||||
maps: Record<string, string | null>;
|
||||
social: Record<string, string | null>;
|
||||
seo: Record<string, string | null>;
|
||||
analytics: Record<string, string | null>;
|
||||
header: Record<string, string | null>;
|
||||
footer: Record<string, string | null>;
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user