Add Torob tags, Meshkee SEO routes, and updated static-image slots.
Product pages emit crawler-readable meta, public URLs use id plus Farsi slugs, and the homepage catalog matches the dashboard slot keys. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
f42c30a4b7
commit
c84147e765
@@ -0,0 +1,72 @@
|
||||
import type { Metadata } from "next";
|
||||
import { notFound, permanentRedirect } from "next/navigation";
|
||||
import { PageHero } from "@/components/PageHero";
|
||||
import { ProductTabs } from "@/components/ProductTabs";
|
||||
import { ProductView } from "@/components/ProductView";
|
||||
import { getComments, getProductById, getStoreItemByProduct } from "@/lib/api";
|
||||
import { productHref, productNameSlug, productTitle } from "@/lib/paths";
|
||||
import { SITE } from "@/lib/site";
|
||||
import { torobMetadata } from "@/lib/torob";
|
||||
|
||||
type Props = {
|
||||
params: Promise<{ id: string; slug: string }>;
|
||||
};
|
||||
|
||||
async function loadProduct(id: string) {
|
||||
return getProductById(id);
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
const { id } = await params;
|
||||
try {
|
||||
const product = await loadProduct(id);
|
||||
if (!product) return { title: SITE.nameFa };
|
||||
|
||||
let storeItem = null;
|
||||
try {
|
||||
storeItem = await getStoreItemByProduct(product.id);
|
||||
} catch {
|
||||
storeItem = null;
|
||||
}
|
||||
|
||||
const title = productTitle(product);
|
||||
return {
|
||||
title: `${title} | ${SITE.nameFa}`,
|
||||
description: product.summary?.trim() || SITE.tagline,
|
||||
alternates: { canonical: productHref(product) },
|
||||
...torobMetadata(product, storeItem),
|
||||
};
|
||||
} catch {
|
||||
return { title: SITE.nameFa };
|
||||
}
|
||||
}
|
||||
|
||||
export default async function ProductDetailsPage({ params }: Props) {
|
||||
const { id, slug } = await params;
|
||||
const product = await loadProduct(id);
|
||||
if (!product) notFound();
|
||||
|
||||
const title = productTitle(product);
|
||||
if (decodeURIComponent(slug) !== productNameSlug(title)) {
|
||||
permanentRedirect(productHref(product));
|
||||
}
|
||||
|
||||
const [storeItem, comments] = await Promise.all([
|
||||
getStoreItemByProduct(product.id),
|
||||
getComments("product", product.id),
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHero
|
||||
title={title}
|
||||
english={product.title}
|
||||
crumbs={[{ name: "محصولات", href: "/products" }, { name: title }]}
|
||||
/>
|
||||
<section className="container-site py-10 md:py-14">
|
||||
<ProductView product={product} variants={storeItem?.variants ?? []} />
|
||||
<ProductTabs productId={product.id} descriptionHtml={product.descriptionHtml} comments={comments} />
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { notFound, permanentRedirect } from "next/navigation";
|
||||
import { getCategoryById, getCategoryBySlug, getProduct, getProductById } from "@/lib/api";
|
||||
import { categoryHref, productHref } from "@/lib/paths";
|
||||
|
||||
/** Legacy `/products/{id-or-slug}` → canonical product or category URL. */
|
||||
export default async function LegacyProductOrCategoryRedirect({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ id: string }>;
|
||||
}) {
|
||||
const { id } = await params;
|
||||
|
||||
const productById = await getProductById(id);
|
||||
if (productById) permanentRedirect(productHref(productById));
|
||||
|
||||
const productBySlug = await getProduct(id);
|
||||
if (productBySlug) permanentRedirect(productHref(productBySlug));
|
||||
|
||||
const categoryById = await getCategoryById(id);
|
||||
if (categoryById) permanentRedirect(categoryHref(categoryById));
|
||||
|
||||
const categoryBySlug = await getCategoryBySlug(id);
|
||||
if (categoryBySlug) permanentRedirect(categoryHref(categoryBySlug));
|
||||
|
||||
notFound();
|
||||
}
|
||||
Reference in New Issue
Block a user