feat(animation-sources): add content-driven guide system
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import type { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
import { AnimationSourceDetailContent } from "@/components/animation-source-detail-content";
|
||||
import { ArticleJsonLd } from "@/components/json-ld";
|
||||
import {
|
||||
getAnimationSourceBySlug,
|
||||
listAnimationSources,
|
||||
} from "@/data/animation-sources";
|
||||
|
||||
const SITE_URL =
|
||||
process.env.NEXT_PUBLIC_SITE_URL || "https://poyrazavsever.com";
|
||||
|
||||
type AnimationSourceDetailPageProps = {
|
||||
params: Promise<{ locale: string; slug: string }>;
|
||||
};
|
||||
|
||||
function getLocalizedPath(locale: string, slug: string) {
|
||||
const localePrefix = locale === "tr" ? "" : `/${locale}`;
|
||||
return `${localePrefix}/animation-sources/${slug}`;
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const sources = await listAnimationSources();
|
||||
return sources.map((source) => ({
|
||||
locale: source.lang,
|
||||
slug: source.slug,
|
||||
}));
|
||||
}
|
||||
|
||||
export const dynamicParams = false;
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: AnimationSourceDetailPageProps): Promise<Metadata> {
|
||||
const { locale, slug } = await params;
|
||||
const source = await getAnimationSourceBySlug(slug, locale);
|
||||
|
||||
if (!source) {
|
||||
return { title: locale === "en" ? "Source not found" : "Kaynak bulunamadı" };
|
||||
}
|
||||
|
||||
const url = `${SITE_URL}${getLocalizedPath(locale, source.slug)}`;
|
||||
|
||||
return {
|
||||
title: source.title,
|
||||
description: source.excerpt,
|
||||
alternates: { canonical: url },
|
||||
openGraph: {
|
||||
title: source.title,
|
||||
description: source.excerpt,
|
||||
url,
|
||||
type: "article",
|
||||
publishedTime: source.date,
|
||||
authors: [source.author],
|
||||
images: [
|
||||
{
|
||||
url: source.coverImage,
|
||||
width: 720,
|
||||
height: 720,
|
||||
alt: source.title,
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: source.title,
|
||||
description: source.excerpt,
|
||||
images: [source.coverImage],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default async function AnimationSourceDetailPage({
|
||||
params,
|
||||
}: AnimationSourceDetailPageProps) {
|
||||
const { locale, slug } = await params;
|
||||
const source = await getAnimationSourceBySlug(slug, locale);
|
||||
|
||||
if (!source) notFound();
|
||||
|
||||
const url = `${SITE_URL}${getLocalizedPath(locale, source.slug)}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ArticleJsonLd
|
||||
title={source.title}
|
||||
description={source.excerpt}
|
||||
url={url}
|
||||
image={source.coverImage}
|
||||
datePublished={source.date}
|
||||
authorName={source.author}
|
||||
/>
|
||||
<AnimationSourceDetailContent source={source} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { Metadata } from "next";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { AnimationSourcesContent } from "@/components/animation-sources-content";
|
||||
import { listAnimationSources } from "@/data/animation-sources";
|
||||
|
||||
type AnimationSourcesPageProps = {
|
||||
params: Promise<{ locale: string }>;
|
||||
};
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: AnimationSourcesPageProps): Promise<Metadata> {
|
||||
const { locale } = await params;
|
||||
const t = await getTranslations({ locale, namespace: "AnimationSources" });
|
||||
|
||||
return {
|
||||
title: t("title"),
|
||||
description: t("description"),
|
||||
alternates: {
|
||||
canonical: "/animation-sources",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default async function AnimationSourcesPage({
|
||||
params,
|
||||
}: AnimationSourcesPageProps) {
|
||||
const { locale } = await params;
|
||||
const [sources, t] = await Promise.all([
|
||||
listAnimationSources(locale),
|
||||
getTranslations({ locale, namespace: "AnimationSources" }),
|
||||
]);
|
||||
|
||||
return (
|
||||
<AnimationSourcesContent
|
||||
sources={sources}
|
||||
labels={{
|
||||
title: t("title"),
|
||||
description: t("description"),
|
||||
empty: t("empty"),
|
||||
itemCount: t("itemCount", { count: sources.length }),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
+21
-2
@@ -1,11 +1,15 @@
|
||||
import { listBlogDetails } from "@/data/blog-detail";
|
||||
import { listAnimationSources } from "@/data/animation-sources";
|
||||
import type { MetadataRoute } from "next";
|
||||
|
||||
const SITE_URL =
|
||||
process.env.NEXT_PUBLIC_SITE_URL || "https://poyrazavsever.com";
|
||||
|
||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
const posts = await listBlogDetails();
|
||||
const [posts, animationSources] = await Promise.all([
|
||||
listBlogDetails(),
|
||||
listAnimationSources(),
|
||||
]);
|
||||
|
||||
const staticRoutes: MetadataRoute.Sitemap = [
|
||||
{
|
||||
@@ -62,6 +66,12 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.4,
|
||||
},
|
||||
{
|
||||
url: `${SITE_URL}/animation-sources`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.7,
|
||||
},
|
||||
];
|
||||
|
||||
const blogRoutes: MetadataRoute.Sitemap = posts.map((post) => ({
|
||||
@@ -71,5 +81,14 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
priority: 0.7,
|
||||
}));
|
||||
|
||||
return [...staticRoutes, ...blogRoutes];
|
||||
const animationSourceRoutes: MetadataRoute.Sitemap = animationSources.map(
|
||||
(source) => ({
|
||||
url: `${SITE_URL}${source.lang === "en" ? "/en" : ""}/animation-sources/${source.slug}`,
|
||||
lastModified: source.date ? new Date(source.date) : new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.6,
|
||||
}),
|
||||
);
|
||||
|
||||
return [...staticRoutes, ...blogRoutes, ...animationSourceRoutes];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user