seo: project pages — canonical URLs, keywords, Twitter card, Service JSON-LD structured data
This commit is contained in:
parent
b822dbee40
commit
575dc752df
1 changed files with 50 additions and 3 deletions
|
|
@ -20,12 +20,42 @@ export async function generateMetadata({
|
||||||
if (!project) return { title: "Project Not Found" };
|
if (!project) return { title: "Project Not Found" };
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: `${project.client} | Zachary Gaetano`,
|
title: `${project.client} | Zachary Gaetano — Broadcast Systems Integration`,
|
||||||
description: project.summary,
|
description: project.summary,
|
||||||
|
keywords: [
|
||||||
|
...project.technologies,
|
||||||
|
project.category,
|
||||||
|
project.client,
|
||||||
|
"broadcast systems integration",
|
||||||
|
"Zachary Gaetano",
|
||||||
|
"Wild Dragon",
|
||||||
|
"Washington DC",
|
||||||
|
"broadcast facility design",
|
||||||
|
],
|
||||||
|
alternates: {
|
||||||
|
canonical: `https://www.wilddragon.net/projects/${slug}`,
|
||||||
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: `${project.client} | Zachary Gaetano`,
|
title: `${project.client} | Zachary Gaetano`,
|
||||||
description: project.summary,
|
description: project.summary,
|
||||||
images: [{ url: project.thumbnail }],
|
url: `https://www.wilddragon.net/projects/${slug}`,
|
||||||
|
siteName: "Wild Dragon",
|
||||||
|
type: "website",
|
||||||
|
locale: "en_US",
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: project.thumbnail,
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
alt: `${project.client} — ${project.category}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
card: "summary_large_image",
|
||||||
|
title: `${project.client} | Zachary Gaetano`,
|
||||||
|
description: project.summary,
|
||||||
|
images: [project.thumbnail],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -42,6 +72,18 @@ export default async function ProjectPage({
|
||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const structuredData = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Service",
|
||||||
|
name: project.title,
|
||||||
|
description: project.summary,
|
||||||
|
serviceType: project.category,
|
||||||
|
provider: {
|
||||||
|
"@id": "https://www.wilddragon.net/#organization",
|
||||||
|
},
|
||||||
|
areaServed: "United States",
|
||||||
|
};
|
||||||
|
|
||||||
// Find previous and next projects for navigation
|
// Find previous and next projects for navigation
|
||||||
const currentIndex = projects.findIndex((p) => p.slug === project.slug);
|
const currentIndex = projects.findIndex((p) => p.slug === project.slug);
|
||||||
const prevProject = currentIndex > 0 ? projects[currentIndex - 1] : null;
|
const prevProject = currentIndex > 0 ? projects[currentIndex - 1] : null;
|
||||||
|
|
@ -50,6 +92,11 @@ export default async function ProjectPage({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="min-h-screen bg-white">
|
<main className="min-h-screen bg-white">
|
||||||
|
<script
|
||||||
|
type="application/ld+json"
|
||||||
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Header bar */}
|
{/* Header bar */}
|
||||||
<nav className="fixed top-0 left-0 right-0 z-50 bg-white/95 backdrop-blur-lg shadow-sm py-3.5">
|
<nav className="fixed top-0 left-0 right-0 z-50 bg-white/95 backdrop-blur-lg shadow-sm py-3.5">
|
||||||
<div className="max-w-6xl mx-auto px-6 flex items-center justify-between">
|
<div className="max-w-6xl mx-auto px-6 flex items-center justify-between">
|
||||||
|
|
@ -95,7 +142,7 @@ export default async function ProjectPage({
|
||||||
<div className="relative h-64 md:h-[28rem] overflow-hidden">
|
<div className="relative h-64 md:h-[28rem] overflow-hidden">
|
||||||
<Image
|
<Image
|
||||||
src={project.thumbnail}
|
src={project.thumbnail}
|
||||||
alt={project.title}
|
alt={`${project.client} — ${project.category} broadcast facility`}
|
||||||
fill
|
fill
|
||||||
className="object-cover"
|
className="object-cover"
|
||||||
sizes="100vw"
|
sizes="100vw"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue