seo done
parent
04fdf51e7b
commit
5501a66957
|
@ -60,7 +60,14 @@ export const generateMetadata = async ({ params }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const viewport = {
|
||||||
|
width: 'device-width',
|
||||||
|
initialScale: 1,
|
||||||
|
maximumScale: 1,
|
||||||
|
userScalable: false,
|
||||||
|
// Also supported but less commonly used
|
||||||
|
// interactiveWidget: 'resizes-visual',
|
||||||
|
}
|
||||||
|
|
||||||
export default async function LocaleLayout({
|
export default async function LocaleLayout({
|
||||||
children,
|
children,
|
||||||
|
|
|
@ -83,17 +83,90 @@ const gql_static = `
|
||||||
query Products($locale:I18NLocaleCode,$start:Int,$limit:Int) {
|
query Products($locale:I18NLocaleCode,$start:Int,$limit:Int) {
|
||||||
products(locale: $locale, pagination: { start: $start, limit: $limit }) {
|
products(locale: $locale, pagination: { start: $start, limit: $limit }) {
|
||||||
slug
|
slug
|
||||||
category {
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
brand {
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const gql_metadata = `
|
||||||
|
query Products($locale:I18NLocaleCode,$slug:String!) {
|
||||||
|
products(filters: { slug: { eqi: $slug } }, locale: $locale) {
|
||||||
|
seo {
|
||||||
|
id
|
||||||
|
metaTitle
|
||||||
|
metaDescription
|
||||||
|
metaRobots
|
||||||
|
canonicalURL
|
||||||
|
metaImage {
|
||||||
|
documentId
|
||||||
|
alternativeText
|
||||||
|
url
|
||||||
|
}
|
||||||
|
openGraph {
|
||||||
|
id
|
||||||
|
ogTitle
|
||||||
|
ogDescription
|
||||||
|
ogUrl
|
||||||
|
ogType
|
||||||
|
ogImage {
|
||||||
|
documentId
|
||||||
|
alternativeText
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
structuredData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
export async function generateMetadata({ params }) {
|
||||||
|
const { locale, slug } = await params;
|
||||||
|
|
||||||
|
// Fetch product SEO data from Strapi GraphQL API
|
||||||
|
const data = await graphql(gql_metadata, {
|
||||||
|
locale,
|
||||||
|
slug,
|
||||||
|
});
|
||||||
|
|
||||||
|
const productSEO = data?.products?.[0]?.seo;
|
||||||
|
if (productSEO) {
|
||||||
|
return {
|
||||||
|
title: productSEO.metaTitle,
|
||||||
|
description: productSEO.metaDescription,
|
||||||
|
robots: productSEO.metaRobots || 'index, follow',
|
||||||
|
// canonical: productSEO.canonicalURL || `https://adhorizonintl.com/product/${slug}`,
|
||||||
|
alternates: {
|
||||||
|
canonical: productSEO.canonicalURL || `https://adhorizonintl.com/product/${slug}`,
|
||||||
|
languages: {
|
||||||
|
'en': `https://adhorizonintl.com/product/${slug}`,
|
||||||
|
'ar-OM': `https://adhorizonintl.com/ar-OM/product/${slug}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
openGraph: {
|
||||||
|
title: productSEO.openGraph.ogTitle || productSEO.metaTitle,
|
||||||
|
description: productSEO.openGraph.ogDescription || productSEO.metaDescription,
|
||||||
|
url: productSEO.openGraph.ogUrl || `https://adhorizonintl.com/product/${slug}`,
|
||||||
|
type: productSEO.openGraph.ogType || 'website',
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: productSEO.openGraph.ogImage.url,
|
||||||
|
alt: productSEO.openGraph.ogImage.alternativeText || 'Product Image',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
card: 'summary_large_image',
|
||||||
|
title: productSEO.metaTitle,
|
||||||
|
description: productSEO.metaDescription,
|
||||||
|
images: [productSEO.metaImage?.url],
|
||||||
|
},
|
||||||
|
// structuredData: productSEO.structuredData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
export async function generateStaticParams() {
|
||||||
|
@ -104,11 +177,9 @@ export async function generateStaticParams() {
|
||||||
})
|
})
|
||||||
const params = [];
|
const params = [];
|
||||||
products.forEach((product) => {
|
products.forEach((product) => {
|
||||||
params.push({ category: product.category.slug, slug: product.slug })
|
params.push({ slug: product.slug })
|
||||||
params.push({ category: product.brand.slug, slug: product.slug })
|
|
||||||
})
|
})
|
||||||
return params;
|
return params;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,114 @@
|
||||||
import CategoriesData from "src/view/Categories";
|
import CategoriesData from "src/view/Categories";
|
||||||
|
|
||||||
|
|
||||||
|
const gql_metadata = `
|
||||||
|
query CategoriesAndBrands($locale:I18NLocaleCode,$slug:String!) {
|
||||||
|
categories(filters: { slug: { eqi: $slug } }, locale: $locale) {
|
||||||
|
seo {
|
||||||
|
id
|
||||||
|
metaTitle
|
||||||
|
metaDescription
|
||||||
|
metaRobots
|
||||||
|
canonicalURL
|
||||||
|
metaImage {
|
||||||
|
documentId
|
||||||
|
alternativeText
|
||||||
|
url
|
||||||
|
}
|
||||||
|
openGraph {
|
||||||
|
id
|
||||||
|
ogTitle
|
||||||
|
ogDescription
|
||||||
|
ogUrl
|
||||||
|
ogType
|
||||||
|
ogImage {
|
||||||
|
documentId
|
||||||
|
alternativeText
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
structuredData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
brands(filters: { slug: { eqi: $slug } }, locale: $locale) {
|
||||||
|
seo {
|
||||||
|
id
|
||||||
|
metaTitle
|
||||||
|
metaDescription
|
||||||
|
metaRobots
|
||||||
|
canonicalURL
|
||||||
|
metaImage {
|
||||||
|
documentId
|
||||||
|
alternativeText
|
||||||
|
url
|
||||||
|
}
|
||||||
|
openGraph {
|
||||||
|
id
|
||||||
|
ogTitle
|
||||||
|
ogDescription
|
||||||
|
ogUrl
|
||||||
|
ogType
|
||||||
|
ogImage {
|
||||||
|
documentId
|
||||||
|
alternativeText
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
structuredData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
|
export async function generateMetadata({ params }) {
|
||||||
|
const { locale, slug } = await params;
|
||||||
|
|
||||||
|
// Fetch product SEO data from Strapi GraphQL API
|
||||||
|
const data = await graphql(gql_metadata, {
|
||||||
|
locale,
|
||||||
|
slug,
|
||||||
|
});
|
||||||
|
|
||||||
|
const productSEO = data?.categories?.[0]?.seo || data?.brands?.[0]?.seo;
|
||||||
|
if (productSEO) {
|
||||||
|
return {
|
||||||
|
title: productSEO.metaTitle,
|
||||||
|
description: productSEO.metaDescription,
|
||||||
|
robots: productSEO.metaRobots || 'index, follow',
|
||||||
|
// canonical: productSEO.canonicalURL || `https://adhorizonintl.com/product/${slug}`,
|
||||||
|
alternates: {
|
||||||
|
canonical: productSEO.canonicalURL || `https://adhorizonintl.com/products/${slug}`,
|
||||||
|
languages: {
|
||||||
|
'en': `https://adhorizonintl.com/products/${slug}`,
|
||||||
|
'ar-OM': `https://adhorizonintl.com/ar-OM/products/${slug}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
openGraph: {
|
||||||
|
title: productSEO.openGraph.ogTitle || productSEO.metaTitle,
|
||||||
|
description: productSEO.openGraph.ogDescription || productSEO.metaDescription,
|
||||||
|
url: productSEO.openGraph.ogUrl || `https://adhorizonintl.com/products/${slug}`,
|
||||||
|
type: productSEO.openGraph.ogType || 'website',
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: productSEO.openGraph.ogImage.url,
|
||||||
|
alt: productSEO.openGraph.ogImage.alternativeText || 'Product Image',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
card: 'summary_large_image',
|
||||||
|
title: productSEO.metaTitle,
|
||||||
|
description: productSEO.metaDescription,
|
||||||
|
images: [productSEO.metaImage?.url],
|
||||||
|
},
|
||||||
|
// structuredData: productSEO.structuredData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const CategoryPage = async ({ params }) => {
|
const CategoryPage = async ({ params }) => {
|
||||||
const _params = await params;
|
const _params = await params;
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -73,7 +73,7 @@ export default function ProductCard({ product }) {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Link href={`/products/${brand?.slug || category?.slug || 'horizon'}/${slug}`.toLowerCase()}
|
<Link href={`/product/${slug}`.toLowerCase()}
|
||||||
className={`w-full flex items-center justify-center px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors ${locale === "en" ? "flex-row" : "flex-row-reverse"}`}>
|
className={`w-full flex items-center justify-center px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors ${locale === "en" ? "flex-row" : "flex-row-reverse"}`}>
|
||||||
{t("Utils.moreDetail")}
|
{t("Utils.moreDetail")}
|
||||||
<ChevronRight className="h-4 w-4 ml-2" />
|
<ChevronRight className="h-4 w-4 ml-2" />
|
||||||
|
|
|
@ -84,7 +84,7 @@ const Navbar = ({ items }) => {
|
||||||
}
|
}
|
||||||
${theme == 1 ? "bg-gray-100 " : " "}`}
|
${theme == 1 ? "bg-gray-100 " : " "}`}
|
||||||
>
|
>
|
||||||
<div className={`w-full ${locale === "en" ? "rtl" : "ltr"} px-4 py-4`}>
|
{/* <div className={`w-full ${locale === "en" ? "rtl" : "ltr"} px-4 py-4`}>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
|
|
||||||
<button onClick={changeLocale} className="mr-2 w-fit px-3 p-2 text-sm bg-white flex rounded-xl ">
|
<button onClick={changeLocale} className="mr-2 w-fit px-3 p-2 text-sm bg-white flex rounded-xl ">
|
||||||
|
@ -92,7 +92,7 @@ const Navbar = ({ items }) => {
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
<div className={`w-full flex p-2 ${locale === "en" ? "ltr" : "rtl"} `}>
|
<div className={`w-full flex p-2 ${locale === "en" ? "ltr" : "rtl"} `}>
|
||||||
|
|
||||||
<div className="size-[75px] relative p-1 bg-white rounded-lg flex flex-col items-center rounded-tl-2xl justify-center my-auto">
|
<div className="size-[75px] relative p-1 bg-white rounded-lg flex flex-col items-center rounded-tl-2xl justify-center my-auto">
|
||||||
|
@ -177,9 +177,9 @@ const Navbar = ({ items }) => {
|
||||||
|
|
||||||
{/* <Link href={"/"} className="w-full"> */}
|
{/* <Link href={"/"} className="w-full"> */}
|
||||||
<div className=" w-full mx-1 flex items-center ">
|
<div className=" w-full mx-1 flex items-center ">
|
||||||
<button onClick={changeLocale} className="mr-2 w-fit px-3 p-2 h-fit text-sm bg-white flex rounded-xl ">
|
{/* <button onClick={changeLocale} className="mr-2 w-fit px-3 p-2 h-fit text-sm bg-white flex rounded-xl ">
|
||||||
<p className="mb-0">{locale === "en" ? "العربیه" : "English"}</p>
|
<p className="mb-0">{locale === "en" ? "العربیه" : "English"}</p>
|
||||||
</button>
|
</button> */}
|
||||||
</div>
|
</div>
|
||||||
<div className=" ">
|
<div className=" ">
|
||||||
<Image
|
<Image
|
||||||
|
|
Loading…
Reference in New Issue