Next.js 14 website with standalone output configured for Docker deployment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
129 lines
4.5 KiB
TypeScript
129 lines
4.5 KiB
TypeScript
import { Metadata } from "next";
|
||
import Image from "next/image";
|
||
import Link from "next/link";
|
||
import { notFound } from "next/navigation";
|
||
import { blogPosts } from "@/lib/constants";
|
||
import { generatePageMetadata } from "@/lib/metadata";
|
||
import { generateBreadcrumbSchema } from "@/lib/structured-data";
|
||
import { Button } from "@/components/shared/Button";
|
||
|
||
interface BlogPostPageProps {
|
||
params: { slug: string };
|
||
}
|
||
|
||
export async function generateStaticParams() {
|
||
return blogPosts.map((post) => ({ slug: post.slug }));
|
||
}
|
||
|
||
export async function generateMetadata({ params }: BlogPostPageProps): Promise<Metadata> {
|
||
const post = blogPosts.find((p) => p.slug === params.slug);
|
||
if (!post) return {};
|
||
return generatePageMetadata({
|
||
title: post.title,
|
||
description: post.excerpt,
|
||
path: `/blog/${post.slug}`,
|
||
});
|
||
}
|
||
|
||
export default function BlogPostPage({ params }: BlogPostPageProps) {
|
||
const post = blogPosts.find((p) => p.slug === params.slug);
|
||
if (!post) notFound();
|
||
|
||
return (
|
||
<>
|
||
<script
|
||
type="application/ld+json"
|
||
dangerouslySetInnerHTML={{
|
||
__html: JSON.stringify(generateBreadcrumbSchema([
|
||
{ name: "Home", url: "https://theanatolianedit.com" },
|
||
{ name: "The Edit", url: "https://theanatolianedit.com/blog" },
|
||
{ name: post.title, url: `https://theanatolianedit.com/blog/${post.slug}` },
|
||
])),
|
||
}}
|
||
/>
|
||
|
||
<article className="pt-28 md:pt-36 pb-16">
|
||
<div className="max-w-3xl mx-auto px-5 sm:px-8">
|
||
<div className="mb-8">
|
||
<Link
|
||
href="/blog"
|
||
className="text-bosphorus text-sm font-semibold hover:underline"
|
||
>
|
||
← Back to The Edit
|
||
</Link>
|
||
</div>
|
||
|
||
<span className="inline-block bg-bosphorus/10 text-bosphorus text-xs font-semibold px-3 py-1.5 rounded-full mb-4">
|
||
{post.category}
|
||
</span>
|
||
|
||
<h1 className="font-display text-3xl md:text-4xl lg:text-5xl font-bold text-deep-nazar leading-tight mb-4">
|
||
{post.title}
|
||
</h1>
|
||
|
||
<div className="flex items-center gap-4 text-sm text-deep-nazar/50 mb-8">
|
||
<span>
|
||
{new Date(post.date).toLocaleDateString("en-US", {
|
||
month: "long",
|
||
day: "numeric",
|
||
year: "numeric",
|
||
})}
|
||
</span>
|
||
<span>·</span>
|
||
<span>{post.readTime}</span>
|
||
</div>
|
||
|
||
<div className="relative aspect-[16/9] rounded-3xl overflow-hidden mb-12">
|
||
<Image
|
||
src={post.image}
|
||
alt={post.title}
|
||
fill
|
||
className="object-cover"
|
||
sizes="(max-width: 768px) 100vw, 800px"
|
||
priority
|
||
/>
|
||
</div>
|
||
|
||
{/* Placeholder article content */}
|
||
<div className="prose prose-lg max-w-none text-deep-nazar/80">
|
||
<p className="text-xl leading-relaxed">
|
||
{post.excerpt}
|
||
</p>
|
||
|
||
<p>
|
||
This is a placeholder article. In production, each blog post would contain
|
||
2,000–3,000 words of original, SEO-optimized content written in The Anatolian
|
||
Edit's signature voice — warm, specific, and sensory.
|
||
</p>
|
||
|
||
<h2 className="font-display text-2xl font-bold text-deep-nazar">
|
||
Why This Matters for Your Trip
|
||
</h2>
|
||
|
||
<p>
|
||
The Asian side of Istanbul isn't an alternative to the European side — it's
|
||
the complement. While the historic peninsula gives you the monuments and the
|
||
museums, the Asian side gives you the life. The breakfasts, the sunsets, the
|
||
boulevards, the neighbourhoods where Istanbul actually lives.
|
||
</p>
|
||
|
||
<p>
|
||
Want to experience it yourself?{" "}
|
||
<Link href="/experiences/the-other-side" className="text-bosphorus font-semibold hover:underline">
|
||
Our signature experience
|
||
</Link>{" "}
|
||
covers the best of the Asian coastline in a single afternoon.
|
||
</p>
|
||
</div>
|
||
|
||
<div className="mt-12 pt-8 border-t border-deep-nazar/10 text-center">
|
||
<p className="text-deep-nazar/60 mb-4">Ready to see the Asian side for yourself?</p>
|
||
<Button href="/experiences" variant="coral" size="lg">
|
||
Explore Our Experiences
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</article>
|
||
</>
|
||
);
|
||
}
|