diff --git a/src/app/projects/layout.tsx b/src/app/projects/layout.tsx new file mode 100644 index 00000000..8115c6b9 --- /dev/null +++ b/src/app/projects/layout.tsx @@ -0,0 +1,25 @@ +import type { Metadata } from "next"; + +export const metadata: Metadata = { + title: "Projects - Umair Jibran", + description: "Explore my portfolio of software projects, full-stack applications, and technical solutions.", + openGraph: { + type: "website", + title: "Projects - Umair Jibran", + description: "Explore my portfolio of software projects, full-stack applications, and technical solutions.", + url: "/projects", + }, + twitter: { + card: "summary", + title: "Projects - Umair Jibran", + description: "Explore my portfolio of software projects, full-stack applications, and technical solutions.", + }, +}; + +export default function ProjectsLayout({ + children, +}: { + children: React.ReactNode; +}) { + return children; +} diff --git a/src/app/writing/[slug]/page.tsx b/src/app/writing/[slug]/page.tsx index b62fab17..e2d58a31 100644 --- a/src/app/writing/[slug]/page.tsx +++ b/src/app/writing/[slug]/page.tsx @@ -5,6 +5,56 @@ import { StoryBody } from "@/components/StoryBody"; import { RelatedStories } from "@/components/RelatedStories"; import { Calendar, ArrowLeft } from "lucide-react"; import Link from "next/link"; +import type { Metadata } from "next"; + +export async function generateMetadata({ params }: { params: { slug: string } }): Promise { + const allStories = [ + ...getAllBlog(), + ...getAllCaseStudies() + ]; + + const story = allStories.find(story => story.slug === params.slug); + + if (!story) { + notFound(); + } + + return { + title: story.title, + description: story.excerpt, + authors: story.author ? [{ name: story.author.name }] : undefined, + openGraph: { + type: "article", + title: story.title, + description: story.excerpt, + url: `/writing/${story.slug}`, + publishedTime: story.date, + authors: story.author ? [story.author.name] : undefined, + tags: story.tags, + images: story.ogImage?.url ? [ + { + url: story.ogImage.url, + width: 1200, + height: 630, + alt: story.title, + } + ] : story.coverImage ? [ + { + url: story.coverImage, + width: 1200, + height: 630, + alt: story.title, + } + ] : undefined, + }, + twitter: { + card: "summary_large_image", + title: story.title, + description: story.excerpt, + images: story.ogImage?.url || story.coverImage, + }, + }; +} export default async function StoryPage({ params }: { params: { slug: string } }) { const allStories = [ diff --git a/src/app/writing/page.tsx b/src/app/writing/page.tsx index 49431e31..353507fd 100644 --- a/src/app/writing/page.tsx +++ b/src/app/writing/page.tsx @@ -2,6 +2,23 @@ import { MoreStories } from "@/components/MoreStories"; import { getAllBlog, getAllCaseStudies } from "@/lib/api"; import Link from "next/link"; import { Rss, Linkedin, PenTool } from "lucide-react"; +import type { Metadata } from "next"; + +export const metadata: Metadata = { + title: "Writing - Umair Jibran", + description: "Technical articles, blog posts, and case studies about software engineering, web development, and the technical challenges I encounter.", + openGraph: { + type: "website", + title: "Writing - Umair Jibran", + description: "Technical articles, blog posts, and case studies about software engineering, web development, and the technical challenges I encounter.", + url: "/writing", + }, + twitter: { + card: "summary", + title: "Writing - Umair Jibran", + description: "Technical articles, blog posts, and case studies about software engineering, web development, and the technical challenges I encounter.", + }, +}; export default function Index() { // Combine and sort all content