update deploy
This commit is contained in:
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