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

381 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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