Files
bogazici-api/prompts/frontend-settings.md
2026-03-27 10:41:54 +03:00

11 KiB
Raw Blame History

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 ı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ı

{
  "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
{
  "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:

// 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:

// 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:

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
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

// 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

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

// middleware.ts veya layout.tsx
if (isEnabled(settings.general.maintenance_mode)) {
  return <MaintenancePage message={settings.general.maintenance_message} />;
}

Cache Stratejisi

Strateji ı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:

// 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:

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)

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