feat: implement programmatic SEO infrastructure with localized service pages

This commit is contained in:
AyrisAI
2026-05-16 00:55:42 +03:00
parent c5703c060d
commit d0a7205f90
5 changed files with 300 additions and 3 deletions

View File

@@ -0,0 +1,175 @@
import { getServiceBySlug, getLocationBySlug, getProjectsByService, getSettings } from "@/app/actions";
import Navbar from "@/components/Navbar";
import Footer from "@/components/Footer";
import Image from "next/image";
import Link from "next/link";
import { ArrowRight, CheckCircle2 } from "lucide-react";
import { Metadata } from "next";
import { notFound } from "next/navigation";
interface Props {
params: Promise<{
slug: string;
location: string;
}>;
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug, location: locationSlug } = await params;
const [service, location] = await Promise.all([
getServiceBySlug(slug),
getLocationBySlug(locationSlug)
]);
if (!service || !location) return {};
const title = `${location.name} ${service.title} | Muğla Dijital`;
const description = `${location.name} bölgesinde profesyonel ${service.title.toLowerCase()} hizmetleri. Muğla Dijital Medya Ajansı ile markanızı zirveye taşıyın.`;
return {
title,
description,
alternates: {
canonical: `/services/${slug}/${locationSlug}`,
}
};
}
export default async function ServiceLocationPage({ params }: Props) {
const { slug, location: locationSlug } = await params;
const [service, location, settings] = await Promise.all([
getServiceBySlug(slug),
getLocationBySlug(locationSlug),
getSettings()
]);
if (!service || !location) {
notFound();
}
const projects = await getProjectsByService(service.title);
return (
<main className="min-h-screen bg-[#f5f5f0] text-black pt-24">
<Navbar />
{/* Hero Section */}
<section className="pt-24 pb-16 px-6 md:px-12 border-b border-black/10">
<div className="max-w-7xl mx-auto">
<span className="text-[10px] tracking-[0.2em] uppercase text-black/40 block mb-6">
{location.name} / {service.title}
</span>
<h1 className="editorial-headline text-4xl md:text-6xl lg:text-[5.5rem] text-black uppercase leading-tight">
{location.name} <br />
<span className="text-primary">{service.title}</span> <br />
Çözümleri.
</h1>
<p className="text-black/40 text-[14px] max-w-2xl leading-relaxed mt-10">
{location.name} bölgesindeki işletmeniz için profesyonel {service.title.toLowerCase()} hizmetleri sunuyoruz.
Markanızın dijital varlığını {location.name} ruhuna uygun, estetik ve stratejik bir dille inşa ediyoruz.
</p>
</div>
</section>
{/* Content & Features */}
<section className="py-24 px-6 md:px-12">
<div className="max-w-7xl mx-auto grid grid-cols-1 md:grid-cols-2 gap-16 items-center">
<div className="space-y-12">
<div>
<h2 className="text-2xl font-light uppercase tracking-widest mb-6">Neler Sunuyoruz?</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{[
"Profesyonel Ekipman",
"Yüksek Çözünürlük",
"Hızlı Teslimat",
"Stratejik Planlama",
"Yaratıcı Kurgu",
"Müşteri Odaklılık"
].map((feature) => (
<div key={feature} className="flex items-center gap-3 text-[12px] text-black/60">
<CheckCircle2 className="w-4 h-4 text-primary" />
{feature}
</div>
))}
</div>
</div>
<div className="prose prose-sm text-black/60 leading-relaxed max-w-none">
<p>
{location.name}, Muğla'nın en değerli bölgelerinden biri olarak kendine has bir kimliğe sahip.
Muğla Dijital olarak biz, bu bölgedeki rekabetin farkındayız ve markanızı öne çıkaracak
<strong> {service.title.toLowerCase()}</strong> stratejilerini hayata geçiriyoruz.
</p>
<p>
{service.description || "Hizmetimiz hakkında detaylı bilgi için bizimle iletişime geçebilirsiniz."}
</p>
</div>
</div>
<div className="relative aspect-square bg-black/5 border border-black/10 overflow-hidden group">
<Image
src={service.image_url || "https://images.unsplash.com/photo-1473968512647-3e447244af8f?q=80&w=800"}
alt={`${location.name} ${service.title}`}
fill
className="object-cover grayscale group-hover:grayscale-0 transition-all duration-700"
/>
</div>
</div>
</section>
{/* Related Projects */}
{projects.length > 0 && (
<section className="py-24 px-6 md:px-12 bg-white/30 border-t border-black/10">
<div className="max-w-7xl mx-auto">
<div className="flex justify-between items-end mb-12">
<div>
<span className="text-[10px] tracking-[0.2em] uppercase text-black/40 block mb-4">Referanslarımız</span>
<h2 className="editorial-headline text-3xl md:text-4xl text-black">
Örnek <span className="text-primary">Çalışmalar</span>
</h2>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{projects.map((project: any) => (
<Link key={project.id} href={`/works/${project.slug}`} className="group block">
<div className="space-y-4">
<div className="relative aspect-video overflow-hidden border border-black/10 bg-black/5">
<Image
src={project.hero_image}
alt={project.title}
fill
className="object-cover transition-transform duration-500 group-hover:scale-105"
/>
</div>
<h3 className="text-[12px] uppercase tracking-wider group-hover:text-primary transition-colors">
{project.title}
</h3>
</div>
</Link>
))}
</div>
</div>
</section>
)}
{/* CTA */}
<section className="py-24 px-6 text-center border-t border-black/10">
<div className="max-w-4xl mx-auto space-y-8">
<h2 className="editorial-headline text-3xl md:text-5xl text-black uppercase">
{location.name} İçin <br /><span className="text-primary">Strateji Geliştirelim.</span>
</h2>
<p className="text-black/40 text-[13px] tracking-[0.1em]">
{location.name} bölgesindeki projeniz için profesyonel destek almaya hazır mısınız?
</p>
<Link href="/contact" className="button-primary mx-auto inline-flex items-center gap-2">
Teklif Al
<ArrowRight className="w-3 h-3" />
</Link>
</div>
</section>
<Footer />
</main>
);
}

View File

@@ -18,7 +18,10 @@ export async function generateMetadata(): Promise<Metadata> {
}
export default async function ServicesPage() {
const services = await sql`SELECT * FROM services ORDER BY display_order ASC`;
const [services, locations] = await Promise.all([
sql`SELECT * FROM services ORDER BY display_order ASC`,
sql`SELECT * FROM locations ORDER BY display_order ASC`
]);
const servicesSchema = {
"@context": "https://schema.org",
@@ -44,7 +47,7 @@ export default async function ServicesPage() {
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(servicesSchema) }}
/>
<ServicesClient services={services || []} />
<ServicesClient services={services || []} locations={locations || []} />
</>
);
}