# 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>;
}
```
