fix: blog empty state + proje detay yarı boş meta tablosu
Claude vision ile localhost'ta yapılan tarama sonrası 2 sorun:
1) Blog sayfası — henüz yazı yoksa empty state küçük bir kart olup
altında ~1000px beyaz alan kalıyordu, footer çok aşağıdaydı.
Çözüm: Empty state için zengin bir layout:
- Gradient hero ('Blog yazılarımızı hazırlıyoruz' + 3 CTA:
keşif görüşmesi / WhatsApp / Telefon)
- 'Bu arada hizmetlerimize göz atın' başlıkla 6 hizmet grid
- 'Site analiz raporu' lead magnet kartı
Yazı geldiğinde otomatik normal grid'e döner.
2) Proje detay sayfası — sağ üstte 2 sütunlu meta card sadece
'Yıl: 2025' gösteriyordu (client_name/industry/duration boş),
yarı boş görünüyor + sağ tarafta büyük boşluk.
Çözüm:
- meta.length >= 2 → eski 2x2 grid card
- meta.length === 1 → inline pill strip altta
- meta yok → grid tek sütuna döner (lg:grid-cols-[1.4fr_1fr]
conditional)
Claude vision ile 10 sayfa screenshot alındı (/tmp/kovak-screenshots).
Diğer sayfalar (anasayfa, hizmet detay, sektör, iletişim, vs)
tasarım açısından temiz görünüyor.
This commit is contained in:
+205
-57
@@ -1,21 +1,179 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
import { ArrowRight, Calendar } from "lucide-react";
|
||||
import {
|
||||
ArrowRight,
|
||||
Calendar,
|
||||
Sparkles,
|
||||
MessageCircle,
|
||||
Phone,
|
||||
FileText,
|
||||
} from "lucide-react";
|
||||
import { SectionTitle } from "@/components/section-title";
|
||||
import { listPublishedPosts } from "@/lib/data";
|
||||
import { Icon } from "@/components/icon";
|
||||
import {
|
||||
getSiteSettings,
|
||||
listPublishedPosts,
|
||||
listServices,
|
||||
} from "@/lib/data";
|
||||
import { buildMetadata } from "@/lib/seo";
|
||||
import { siteConfig } from "@/lib/site-config";
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
return buildMetadata("/blog", {
|
||||
title: "Blog",
|
||||
description: "Yazılım, web tasarım, SEO ve dijital pazarlama üzerine yazılar.",
|
||||
description:
|
||||
"Yazılım, web tasarım, SEO ve dijital pazarlama üzerine yazılar.",
|
||||
});
|
||||
}
|
||||
|
||||
export default async function BlogIndex() {
|
||||
const posts = await listPublishedPosts();
|
||||
const [posts, services, settings] = await Promise.all([
|
||||
listPublishedPosts(),
|
||||
listServices(),
|
||||
getSiteSettings(),
|
||||
]);
|
||||
|
||||
const phoneRaw = settings?.contact_phone_raw ?? siteConfig.contact.phoneRaw;
|
||||
const phone = settings?.contact_phone ?? siteConfig.contact.phone;
|
||||
const wa = phoneRaw.replace(/[^\d]/g, "");
|
||||
const waMessage = settings?.whatsapp_message ?? "";
|
||||
const waHref = `https://wa.me/${wa}${
|
||||
waMessage ? `?text=${encodeURIComponent(waMessage)}` : ""
|
||||
}`;
|
||||
|
||||
// Empty state — site daha yeni, içerik yok
|
||||
if (posts.length === 0) {
|
||||
return (
|
||||
<div className="mx-auto max-w-7xl px-6 py-20">
|
||||
<SectionTitle
|
||||
eyebrow="Blog"
|
||||
title="Yazılım, tasarım ve büyüme üzerine"
|
||||
description="Sektörden notlar, vaka çalışmaları ve teknik rehberler."
|
||||
/>
|
||||
|
||||
{/* Coming soon hero */}
|
||||
<div className="relative mt-14 overflow-hidden rounded-3xl bg-gradient-to-br from-[var(--navy)] via-[var(--sky-600)] to-[var(--sky)] p-12 text-center text-white shadow-xl shadow-[var(--navy)]/15">
|
||||
<div
|
||||
className="absolute inset-0 opacity-10"
|
||||
style={{
|
||||
backgroundImage:
|
||||
"radial-gradient(circle at 1px 1px, white 1px, transparent 0)",
|
||||
backgroundSize: "24px 24px",
|
||||
}}
|
||||
aria-hidden
|
||||
/>
|
||||
<div className="relative">
|
||||
<span className="inline-flex items-center gap-2 rounded-full border border-white/30 bg-white/10 px-4 py-1.5 text-xs font-medium backdrop-blur">
|
||||
<Sparkles className="size-3.5" />
|
||||
Yakında
|
||||
</span>
|
||||
<h2 className="mt-5 text-3xl font-bold sm:text-4xl">
|
||||
Blog yazılarımızı hazırlıyoruz
|
||||
</h2>
|
||||
<p className="mx-auto mt-4 max-w-xl text-base text-white/80">
|
||||
Sektörden vaka çalışmaları, teknik rehberler ve sahada öğrendiklerimizi
|
||||
kısa sürede burada paylaşmaya başlayacağız.
|
||||
</p>
|
||||
|
||||
<div className="mt-8 flex flex-wrap items-center justify-center gap-3">
|
||||
<Link
|
||||
href="/iletisim"
|
||||
className="inline-flex items-center gap-2 rounded-xl bg-white px-5 py-3 text-sm font-semibold text-[var(--navy)] transition hover:-translate-y-0.5 hover:bg-blue-50"
|
||||
>
|
||||
Ücretsiz keşif görüşmesi
|
||||
<ArrowRight className="size-4" />
|
||||
</Link>
|
||||
<a
|
||||
href={waHref}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 rounded-xl bg-[#25d366] px-5 py-3 text-sm font-semibold text-white transition hover:-translate-y-0.5 hover:bg-[#1ebe5d]"
|
||||
>
|
||||
<MessageCircle className="size-4" />
|
||||
WhatsApp
|
||||
</a>
|
||||
<a
|
||||
href={`tel:${phoneRaw}`}
|
||||
className="inline-flex items-center gap-2 rounded-xl border border-white/30 bg-white/5 px-5 py-3 text-sm font-semibold text-white backdrop-blur transition hover:border-white/60"
|
||||
>
|
||||
<Phone className="size-4" />
|
||||
{phone}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bu arada → Hizmetler grid */}
|
||||
{services.length > 0 && (
|
||||
<section className="mt-16">
|
||||
<div className="flex items-end justify-between">
|
||||
<SectionTitle
|
||||
align="left"
|
||||
eyebrow="Bu arada"
|
||||
title="Hizmetlerimize göz atın"
|
||||
description="Blog yazılarımızı beklerken sunduğumuz çözümleri keşfedin."
|
||||
/>
|
||||
<Link
|
||||
href="/hizmetler"
|
||||
className="hidden text-sm font-medium text-[var(--sky-600)] hover:text-[var(--navy)] sm:inline-flex"
|
||||
>
|
||||
Tümünü gör →
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{services.slice(0, 6).map((s) => (
|
||||
<Link
|
||||
key={s.slug}
|
||||
href={`/hizmetler/${s.slug}`}
|
||||
className="group relative overflow-hidden rounded-2xl border border-[var(--border)] bg-white p-6 transition-all duration-300 hover:-translate-y-1 hover:border-[var(--sky)]/40 hover:shadow-xl hover:shadow-[var(--navy)]/10"
|
||||
>
|
||||
<div className="flex size-12 items-center justify-center rounded-xl bg-gradient-to-br from-[var(--sky)] to-purple-500 text-white shadow-lg">
|
||||
<Icon name={s.icon} className="size-5" />
|
||||
</div>
|
||||
<h3 className="mt-5 text-base font-bold text-[var(--navy)] group-hover:text-[var(--sky-600)]">
|
||||
{s.title}
|
||||
</h3>
|
||||
<p className="mt-2 text-sm leading-relaxed text-[var(--muted)] line-clamp-2">
|
||||
{s.description}
|
||||
</p>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Lead magnet CTA */}
|
||||
<section className="mt-16 rounded-2xl border border-dashed border-[var(--sky)]/40 bg-[var(--sky-50)]/40 p-8 sm:p-10">
|
||||
<div className="flex flex-col items-start gap-6 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-[var(--sky-600)]">
|
||||
<FileText className="mr-1 inline size-3.5" />
|
||||
Ücretsiz Rapor
|
||||
</p>
|
||||
<h3 className="mt-2 text-2xl font-bold text-[var(--navy)]">
|
||||
Site analiz raporunuzu alın
|
||||
</h3>
|
||||
<p className="mt-2 max-w-xl text-sm text-[var(--muted)]">
|
||||
Mevcut sitenizin SEO, hız, mobil ve dönüşüm performansını ücretsiz
|
||||
değerlendirelim. 24 saat içinde detaylı rapor e-postanızda.
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/site-analizi"
|
||||
className="inline-flex items-center gap-2 rounded-xl bg-[var(--navy)] px-6 py-3 text-sm font-semibold text-white shadow-lg shadow-[var(--navy)]/20 transition hover:-translate-y-0.5 hover:bg-[var(--navy-700)]"
|
||||
>
|
||||
Ücretsiz raporumu istiyorum
|
||||
<ArrowRight className="size-4" />
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Normal grid — yazı varsa
|
||||
return (
|
||||
<div className="mx-auto max-w-7xl px-6 py-20">
|
||||
<SectionTitle
|
||||
@@ -24,60 +182,50 @@ export default async function BlogIndex() {
|
||||
description="Sektörden notlar, vaka çalışmaları ve teknik rehberler."
|
||||
/>
|
||||
|
||||
<div className="mt-14">
|
||||
{posts.length === 0 ? (
|
||||
<div className="rounded-2xl border border-dashed border-[var(--border)] bg-[var(--navy-50)]/40 p-12 text-center">
|
||||
<p className="text-sm text-[var(--muted)]">
|
||||
Henüz yayınlanmış yazı yok.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
||||
{posts.map((p) => (
|
||||
<article
|
||||
key={p.$id}
|
||||
className="group overflow-hidden rounded-2xl border border-[var(--border)] bg-white transition hover:shadow-lg"
|
||||
>
|
||||
<Link href={`/blog/${p.slug}`}>
|
||||
<div className="relative aspect-video overflow-hidden bg-[var(--navy-50)]">
|
||||
{p.cover_image ? (
|
||||
<Image
|
||||
src={p.cover_image}
|
||||
alt={p.title}
|
||||
fill
|
||||
sizes="(min-width: 1024px) 33vw, (min-width: 768px) 50vw, 100vw"
|
||||
className="object-cover transition group-hover:scale-105"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-full items-center justify-center text-3xl font-bold text-[var(--navy)]/30">
|
||||
{p.title.charAt(0)}
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-14 grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
||||
{posts.map((p) => (
|
||||
<article
|
||||
key={p.$id}
|
||||
className="group overflow-hidden rounded-2xl border border-[var(--border)] bg-white transition hover:shadow-lg"
|
||||
>
|
||||
<Link href={`/blog/${p.slug}`}>
|
||||
<div className="relative aspect-video overflow-hidden bg-[var(--navy-50)]">
|
||||
{p.cover_image ? (
|
||||
<Image
|
||||
src={p.cover_image}
|
||||
alt={p.title}
|
||||
fill
|
||||
sizes="(min-width: 1024px) 33vw, (min-width: 768px) 50vw, 100vw"
|
||||
className="object-cover transition group-hover:scale-105"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-full items-center justify-center text-3xl font-bold text-[var(--navy)]/30">
|
||||
{p.title.charAt(0)}
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<p className="flex items-center gap-1.5 text-xs text-[var(--muted)]">
|
||||
<Calendar className="size-3.5" />
|
||||
{p.published_at
|
||||
? new Date(p.published_at).toLocaleDateString("tr-TR")
|
||||
: "—"}
|
||||
</p>
|
||||
<h3 className="mt-2 text-lg font-semibold text-[var(--navy)] group-hover:text-[var(--sky-600)]">
|
||||
{p.title}
|
||||
</h3>
|
||||
{p.excerpt && (
|
||||
<p className="mt-2 text-sm leading-relaxed text-[var(--muted)] line-clamp-3">
|
||||
{p.excerpt}
|
||||
</p>
|
||||
)}
|
||||
<span className="mt-4 inline-flex items-center gap-1 text-sm font-medium text-[var(--sky-600)]">
|
||||
Devamını oku <ArrowRight className="size-3.5" />
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<p className="flex items-center gap-1.5 text-xs text-[var(--muted)]">
|
||||
<Calendar className="size-3.5" />
|
||||
{p.published_at
|
||||
? new Date(p.published_at).toLocaleDateString("tr-TR")
|
||||
: "—"}
|
||||
</p>
|
||||
<h3 className="mt-2 text-lg font-semibold text-[var(--navy)] group-hover:text-[var(--sky-600)]">
|
||||
{p.title}
|
||||
</h3>
|
||||
{p.excerpt && (
|
||||
<p className="mt-2 text-sm leading-relaxed text-[var(--muted)] line-clamp-3">
|
||||
{p.excerpt}
|
||||
</p>
|
||||
)}
|
||||
<span className="mt-4 inline-flex items-center gap-1 text-sm font-medium text-[var(--sky-600)]">
|
||||
Devamını oku <ArrowRight className="size-3.5" />
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user