1444aa3995
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.
108 lines
4.3 KiB
TypeScript
108 lines
4.3 KiB
TypeScript
import Image from "next/image";
|
||
import Link from "next/link";
|
||
import { ArrowRight, Sparkles } from "lucide-react";
|
||
import type { SiteSettingsRow, StatItem } from "@/lib/types";
|
||
|
||
const DEFAULT_STATS: StatItem[] = [
|
||
{ value: "50+", label: "Tamamlanan proje" },
|
||
{ value: "10+", label: "Yıllık deneyim" },
|
||
{ value: "24/7", label: "Teknik destek" },
|
||
];
|
||
|
||
function parseStats(items?: string[] | null): StatItem[] {
|
||
if (!items || items.length === 0) return DEFAULT_STATS;
|
||
const out: StatItem[] = [];
|
||
for (const raw of items) {
|
||
try {
|
||
const obj = JSON.parse(raw) as Partial<StatItem>;
|
||
if (obj.value && obj.label) out.push({ value: obj.value, label: obj.label });
|
||
} catch {
|
||
/* ignore */
|
||
}
|
||
}
|
||
return out.length > 0 ? out : DEFAULT_STATS;
|
||
}
|
||
|
||
export function Hero({ settings }: { settings?: SiteSettingsRow | null }) {
|
||
const badge = settings?.hero_badge ?? "Kocaeli'nin teknoloji ajansı";
|
||
const title =
|
||
settings?.hero_title ?? "Fikirden ürüne tek bir partner ile yola çıkın";
|
||
const subtitle =
|
||
settings?.hero_subtitle ??
|
||
"Web, mobil ve CRM çözümlerinde uçtan uca geliştirme. Markanıza özel tasarım, ölçeklenebilir altyapı ve uzun vadeli destek.";
|
||
const primaryLabel =
|
||
settings?.hero_cta_primary_label ?? "Proje görüşmesi başlat";
|
||
const primaryHref = settings?.hero_cta_primary_href ?? "/iletisim";
|
||
const secondaryLabel =
|
||
settings?.hero_cta_secondary_label ?? "Hizmetlerimizi inceleyin";
|
||
const secondaryHref = settings?.hero_cta_secondary_href ?? "/hizmetler";
|
||
const stats = parseStats(settings?.hero_stats);
|
||
|
||
return (
|
||
<section className="relative overflow-hidden">
|
||
<div className="absolute inset-0 hero-grid opacity-60" aria-hidden />
|
||
<div className="absolute -right-32 top-1/2 -z-0 size-[520px] -translate-y-1/2 rounded-full bg-gradient-to-br from-[var(--sky)]/30 to-[var(--navy)]/0 blur-3xl" aria-hidden />
|
||
|
||
<div className="relative mx-auto grid max-w-7xl items-center gap-12 px-6 py-24 md:grid-cols-2 md:py-32">
|
||
<div>
|
||
<span className="inline-flex items-center gap-2 rounded-full border border-[var(--sky)]/30 bg-[var(--sky-50)] px-3 py-1 text-xs font-medium text-[var(--sky-600)]">
|
||
<Sparkles className="size-3.5" />
|
||
{badge}
|
||
</span>
|
||
|
||
<h1 className="mt-6 text-4xl font-bold leading-tight tracking-tight text-[var(--navy)] sm:text-5xl md:text-6xl">
|
||
{title}
|
||
</h1>
|
||
|
||
<p className="mt-6 max-w-xl text-lg leading-relaxed text-[var(--muted)]">
|
||
{subtitle}
|
||
</p>
|
||
|
||
<div className="mt-8 flex flex-col gap-3 sm:flex-row">
|
||
<Link
|
||
href={primaryHref}
|
||
className="inline-flex items-center justify-center gap-2 rounded-full bg-[var(--navy)] px-6 py-3 text-sm font-medium text-white transition hover:bg-[var(--navy-700)]"
|
||
>
|
||
{primaryLabel}
|
||
<ArrowRight className="size-4" />
|
||
</Link>
|
||
<Link
|
||
href={secondaryHref}
|
||
className="inline-flex items-center justify-center gap-2 rounded-full border border-[var(--border)] bg-white px-6 py-3 text-sm font-medium text-[var(--navy)] transition hover:border-[var(--navy)]"
|
||
>
|
||
{secondaryLabel}
|
||
</Link>
|
||
</div>
|
||
|
||
{stats.length > 0 && (
|
||
<dl className="mt-12 grid max-w-md grid-cols-3 gap-6">
|
||
{stats.map((stat) => (
|
||
<div key={stat.label}>
|
||
<dt className="text-2xl font-bold text-[var(--navy)]">
|
||
{stat.value}
|
||
</dt>
|
||
<dd className="mt-1 text-xs text-[var(--muted)]">{stat.label}</dd>
|
||
</div>
|
||
))}
|
||
</dl>
|
||
)}
|
||
</div>
|
||
|
||
<div className="relative flex justify-center">
|
||
<div className="absolute inset-8 -z-10 rounded-full bg-gradient-to-br from-[var(--sky-50)] to-white blur-2xl" aria-hidden />
|
||
<div className="animate-float">
|
||
<Image
|
||
src="/logo.png"
|
||
alt="Kovak Yazılım"
|
||
width={420}
|
||
height={420}
|
||
priority
|
||
className="size-[320px] object-contain drop-shadow-[0_30px_50px_rgba(15,44,92,0.25)] md:size-[420px]"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|