Files
web/app/ads/page.jsx
Husanjonazamov 64af77101f classify web
2026-02-24 12:52:49 +05:00

216 lines
6.3 KiB
JavaScript

import StructuredData from "@/components/Layout/StructuredData";
import Products from "@/components/PagesComponent/Ads/Ads";
import { SEO_REVALIDATE_SECONDS } from "@/lib/constants";
import { generateKeywords } from "@/utils/generateKeywords";
export const dynamic = "force-dynamic";
const buildIndexableParams = (searchParams) => {
const indexableFilters = [
"category",
"query",
"country",
"state",
"city",
"areaId",
"sortBy",
"min_price",
"max_price",
"date_posted",
];
const params = new URLSearchParams();
const { country, state, city, areaId } = searchParams || {};
const locationPriority = areaId
? "areaId"
: city
? "city"
: state
? "state"
: country
? "country"
: null;
indexableFilters.forEach((key) => {
let value = searchParams[key];
if (value === undefined || value === "") return;
// 🧹 Skip non-selected location levels
if (["country", "state", "city", "areaId"].includes(key)) {
if (key !== locationPriority) return;
}
if (["areaId", "min_price", "max_price"].includes(key))
value = Number(value);
if (key === "category") params.append("category_slug", value);
else if (key === "query") params.append("search", value);
else if (key === "date_posted") params.append("posted_since", value);
else params.append(key, value);
});
return params.toString();
};
const buildCanonicalParams = (searchParams) => {
const params = new URLSearchParams();
const { category, query, lang } = searchParams || {};
if (category) params.append("category", category);
if (query) params.append("search", query);
// Add lang to canonical params for consistency with sitemap
if (lang) params.append("lang", lang);
return params.toString();
};
export const generateMetadata = async ({ searchParams }) => {
if (process.env.NEXT_PUBLIC_SEO === "false") return;
try {
const originalSearchParams = await searchParams;
const langCode = originalSearchParams?.lang || "en";
const slug = originalSearchParams?.category || ""; // change to your param name if needed
let title = process.env.NEXT_PUBLIC_META_TITLE;
let description = process.env.NEXT_PUBLIC_META_DESCRIPTION;
let keywords = process.env.NEXT_PUBLIC_META_kEYWORDS;
let image = "";
if (slug) {
// Fetch category-specific SEO
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}${process.env.NEXT_PUBLIC_END_POINT}get-categories?slug=${slug}`,
{
headers: { "Content-Language": langCode || "en" },
next: {
revalidate: SEO_REVALIDATE_SECONDS,
},
}
);
const data = await response.json();
const selfCategory = data?.self_category;
title = selfCategory?.translated_name || title;
description = selfCategory?.translated_description || description;
keywords =
generateKeywords(selfCategory?.translated_description) || keywords;
image = selfCategory?.image || image;
} else {
// Fetch default ad listing SEO
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}${process.env.NEXT_PUBLIC_END_POINT}seo-settings?page=ad-listing`,
{
headers: { "Content-Language": langCode || "en" },
next: {
revalidate: SEO_REVALIDATE_SECONDS,
},
}
);
const data = await res.json();
const adListing = data?.data?.[0];
title = adListing?.translated_title || title;
description = adListing?.translated_description || description;
keywords = adListing?.translated_keywords || keywords;
image = adListing?.image || image;
}
const baseUrl = process.env.NEXT_PUBLIC_WEB_URL;
const paramsStr = buildCanonicalParams(originalSearchParams);
const canonicalUrl = `${baseUrl}/ads${paramsStr ? `?${paramsStr}` : ""}`;
return {
title,
description,
openGraph: {
images: image ? [image] : [],
},
keywords,
alternates: {
canonical: canonicalUrl,
},
};
} catch (error) {
console.error("Error fetching MetaData:", error);
return null;
}
};
const getAllItems = async (langCode, searchParams) => {
if (process.env.NEXT_PUBLIC_SEO === "false") return;
try {
const queryString = buildIndexableParams(searchParams)
? buildIndexableParams(searchParams)
: "";
const url = `${process.env.NEXT_PUBLIC_API_URL}${process.env.NEXT_PUBLIC_END_POINT
}get-item?page=1${queryString ? `&${queryString}` : ""}`;
const res = await fetch(url, {
headers: {
"Content-Language": langCode || "en",
},
next: {
revalidate: SEO_REVALIDATE_SECONDS,
},
});
const data = await res.json();
return data?.data?.data || [];
} catch (error) {
console.error("Error fetching Product Items Data:", error);
return [];
}
};
const AdsPage = async ({ searchParams }) => {
const originalSearchParams = await searchParams;
const langCode = originalSearchParams?.lang || "en";
const AllItems = await getAllItems(langCode, originalSearchParams);
const jsonLd = AllItems
? {
"@context": "https://schema.org",
"@type": "ItemList",
itemListElement: AllItems.map((product, index) => ({
"@type": "ListItem",
position: index + 1, // Position starts at 1
item: {
"@type": "Product",
productID: product?.id,
name: product?.translated_item?.name || "",
description: product?.translated_item?.description || "",
image: product?.image || "",
url: `${process.env.NEXT_PUBLIC_WEB_URL}/ad-details/${product?.slug}`,
category: {
"@type": "Thing",
name: product?.category?.translated_name || "",
},
offers: {
"@type": "Offer",
price: product.price || undefined,
priceCurrency: product?.price ? "USD" : undefined,
availability: product?.price
? "https://schema.org/InStock"
: "https://schema.org/PreOrder",
},
countryOfOrigin: product?.translated_item?.country || "",
},
})),
}
: null;
return (
<>
<StructuredData data={jsonLd} />
<Products searchParams={originalSearchParams} />
</>
);
};
export default AdsPage;