feat: anasayfa içeriği, iletişim ve sosyal medya yönetilebilir

Yeni site_settings tablosu (singleton, rowId='homepage'):
- Hero: badge, title, subtitle, 2 CTA (label+href), stats (JSON array)
- Section başlıkları: services/projects/testimonials eyebrow + title + description
- Alt CTA: title, description, button label+href
- Contact: phone (görünen + tel: ham), email, address, hafta içi/sonu saatleri
- Social: linkedin/instagram/twitter/facebook URL'leri
- Footer tagline

Mevcut hardcoded değerler seed edildi.

Admin:
- /admin/site sayfası eklendi (sidebar'a 'Site Ayarları' linki)
- Bölümlü tek form: Hero / Hizmetler / Projeler / Referanslar / Alt CTA / İletişim / Sosyal / Footer
- Stats için 'değer | etiket' satır formatı

Public bağlantılar:
- Hero component artık settings prop alıyor (fallback değerlerle)
- Anasayfa: tüm section başlıkları ve alt CTA settings'ten geliyor
- Header: telefon settings'ten
- Footer: tagline, adres, telefon, email, sosyal linkler settings'ten
  (sosyal link sadece dolu olanlar gösteriliyor)
- Footer'da hizmetler artık /hizmetler/[slug] detay sayfalarına bağlı
- İletişim sayfası: adres, telefon, email, saatler settings'ten

30 route üretiliyor.
This commit is contained in:
Ege Can Komur
2026-05-20 02:56:45 +03:00
parent c0da5ae8d3
commit 1444aa3995
11 changed files with 621 additions and 91 deletions
+56 -40
View File
@@ -1,10 +1,33 @@
import Image from "next/image";
import Link from "next/link";
import { siteConfig } from "@/lib/site-config";
import { Mail, MapPin, Phone } from "lucide-react";
import { LinkedinIcon, InstagramIcon, TwitterIcon } from "@/components/social-icons";
import { getSiteSettings, listServices } from "@/lib/data";
import { siteConfig } from "@/lib/site-config";
export async function Footer() {
const [settings, services] = await Promise.all([
getSiteSettings(),
listServices(),
]);
const tagline = settings?.footer_tagline ?? siteConfig.tagline;
const address = settings?.contact_address ?? siteConfig.contact.address;
const phone = settings?.contact_phone ?? siteConfig.contact.phone;
const phoneRaw = settings?.contact_phone_raw ?? siteConfig.contact.phoneRaw;
const email = settings?.contact_email ?? siteConfig.contact.email;
const socials = [
{ url: settings?.social_linkedin, label: "LinkedIn", Icon: LinkedinIcon },
{ url: settings?.social_instagram, label: "Instagram", Icon: InstagramIcon },
{ url: settings?.social_twitter, label: "Twitter / X", Icon: TwitterIcon },
].filter((s) => s.url);
const footerServices =
services.length > 0
? services.slice(0, 5)
: siteConfig.fallbackServices.slice(0, 5);
export function Footer() {
return (
<footer className="mt-24 border-t border-[var(--border)] bg-[var(--navy)] text-white">
<div className="mx-auto grid max-w-7xl gap-10 px-6 py-14 md:grid-cols-4">
@@ -19,9 +42,7 @@ export function Footer() {
/>
<span className="text-lg font-semibold">{siteConfig.name}</span>
</div>
<p className="mt-4 text-sm leading-relaxed text-white/70">
{siteConfig.tagline}
</p>
<p className="mt-4 text-sm leading-relaxed text-white/70">{tagline}</p>
</div>
<div>
@@ -29,9 +50,9 @@ export function Footer() {
Hizmetler
</h3>
<ul className="mt-4 space-y-2 text-sm text-white/70">
{siteConfig.fallbackServices.slice(0, 5).map((s) => (
{footerServices.map((s) => (
<li key={s.slug}>
<Link href={`/hizmetler#${s.slug}`} className="hover:text-white">
<Link href={`/hizmetler/${s.slug}`} className="hover:text-white">
{s.title}
</Link>
</li>
@@ -46,56 +67,51 @@ export function Footer() {
<ul className="mt-4 space-y-3 text-sm text-white/70">
<li className="flex items-start gap-2">
<MapPin className="mt-0.5 size-4 shrink-0" />
<span>{siteConfig.contact.address}</span>
<span>{address}</span>
</li>
<li className="flex items-center gap-2">
<Phone className="size-4 shrink-0" />
<a href={`tel:${siteConfig.contact.phoneRaw}`} className="hover:text-white">
{siteConfig.contact.phone}
<a href={`tel:${phoneRaw}`} className="hover:text-white">
{phone}
</a>
</li>
<li className="flex items-center gap-2">
<Mail className="size-4 shrink-0" />
<a href={`mailto:${siteConfig.contact.email}`} className="hover:text-white">
{siteConfig.contact.email}
<a href={`mailto:${email}`} className="hover:text-white">
{email}
</a>
</li>
</ul>
</div>
<div>
<h3 className="text-sm font-semibold uppercase tracking-wider text-white/80">
Sosyal Medya
</h3>
<div className="mt-4 flex gap-3">
<a
href={siteConfig.social.linkedin}
aria-label="LinkedIn"
className="flex size-9 items-center justify-center rounded-full bg-white/10 transition hover:bg-white/20"
>
<LinkedinIcon className="size-4" />
</a>
<a
href={siteConfig.social.instagram}
aria-label="Instagram"
className="flex size-9 items-center justify-center rounded-full bg-white/10 transition hover:bg-white/20"
>
<InstagramIcon className="size-4" />
</a>
<a
href={siteConfig.social.twitter}
aria-label="Twitter"
className="flex size-9 items-center justify-center rounded-full bg-white/10 transition hover:bg-white/20"
>
<TwitterIcon className="size-4" />
</a>
{socials.length > 0 && (
<div>
<h3 className="text-sm font-semibold uppercase tracking-wider text-white/80">
Sosyal Medya
</h3>
<div className="mt-4 flex gap-3">
{socials.map(({ url, label, Icon }) => (
<a
key={label}
href={url as string}
target="_blank"
rel="noopener noreferrer"
aria-label={label}
className="flex size-9 items-center justify-center rounded-full bg-white/10 transition hover:bg-white/20"
>
<Icon className="size-4" />
</a>
))}
</div>
</div>
</div>
)}
</div>
<div className="border-t border-white/10">
<div className="mx-auto flex max-w-7xl flex-col items-center justify-between gap-2 px-6 py-5 text-xs text-white/60 md:flex-row">
<p>© {new Date().getFullYear()} {siteConfig.name}. Tüm hakları saklıdır.</p>
<p>
© {new Date().getFullYear()} {siteConfig.name}. Tüm hakları saklıdır.
</p>
<p>Kocaeli, Türkiye</p>
</div>
</div>