Refactor blog components for improved tag filtering and related content display
- Added `getBlogTagFacets` API call to fetch blog tags based on city scope. - Updated `BlogsPage` to read selected tag from URL and handle tag changes with URL updates. - Modified `Title` component to display tags from the new API and reflect active tag state. - Enhanced breadcrumb navigation to link categories to their respective pages. - Adjusted related content section to display articles from the same category and fixed layout issues. - Corrected heading hierarchy across various components for better SEO compliance. - Ensured consistent styling and spacing in related content items.
This commit is contained in:
+32
-6
@@ -128,12 +128,28 @@ async function Blog({ params }) {
|
||||
// پستِ شهر دیگر نشان داده میشود که حالا ۴۰۴ میگیرد.
|
||||
const { matchedCity, isRoot } = await getStateInfo();
|
||||
const scopeCityId = domainScopeCityId({ matchedCity, isRoot });
|
||||
let relatedBlogs = [];
|
||||
const relatedResponse = await fetchReq(
|
||||
`${API_URL}/api/v1/blogs?page=1&limit=6${scopeCityId != null ? `&city_id=${scopeCityId}` : ""}`
|
||||
const scopeQuery = scopeCityId != null ? `&city_id=${scopeCityId}` : "";
|
||||
|
||||
// دستهٔ مقاله = تگ اول؛ هم بردکرامب و هم مقالات مرتبط از همین میآیند.
|
||||
const categoryName = Array.isArray(blog?.tag) ? blog.tag[0]?.name : null;
|
||||
|
||||
// اول همدستهها؛ دستههای کمپست (مثلاً «قلب و عروق» با یک مقاله) با آخرین
|
||||
// مقالات پر میشوند تا ستون خالی یا کوتاه نماند.
|
||||
const sameTagResponse = categoryName
|
||||
? await fetchReq(
|
||||
`${API_URL}/api/v1/blogs?page=1&limit=6&tag=${encodeURIComponent(categoryName)}${scopeQuery}`
|
||||
)
|
||||
: null;
|
||||
const latestResponse = await fetchReq(
|
||||
`${API_URL}/api/v1/blogs?page=1&limit=6${scopeQuery}`
|
||||
);
|
||||
relatedBlogs = (relatedResponse?.data || [])
|
||||
.filter((b) => b.uuid !== blog?.uuid)
|
||||
|
||||
const seen = new Set([blog?.uuid]);
|
||||
const relatedBlogs = [
|
||||
...(sameTagResponse?.data || []),
|
||||
...(latestResponse?.data || []),
|
||||
]
|
||||
.filter((b) => !seen.has(b.uuid) && seen.add(b.uuid))
|
||||
.slice(0, 4)
|
||||
.map(normalizeBlog);
|
||||
|
||||
@@ -188,8 +204,18 @@ async function Blog({ params }) {
|
||||
itemListElement: [
|
||||
{ "@type": "ListItem", position: 1, name: "خانه", item: origin },
|
||||
{ "@type": "ListItem", position: 2, name: "مقالات", item: `${origin}/blogs` },
|
||||
// دستهٔ مقاله = همان چیزی که بردکرامب HTML نشان میدهد؛ مقالهٔ بدون تگ
|
||||
// سهسطحی میماند، چون سطح خالی کل BreadcrumbList را نامعتبر میکند.
|
||||
...(categoryName
|
||||
? [{
|
||||
"@type": "ListItem",
|
||||
position: 3,
|
||||
name: categoryName,
|
||||
item: `${origin}/blogs?tag=${encodeURIComponent(categoryName)}`,
|
||||
}]
|
||||
: []),
|
||||
// آیتم آخر بدون `item` کل BreadcrumbList را نامعتبر میکند
|
||||
{ "@type": "ListItem", position: 3, name: blog.title,
|
||||
{ "@type": "ListItem", position: categoryName ? 4 : 3, name: blog.title,
|
||||
item: `${origin}/blog/${slug}` },
|
||||
],
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,3 +1,4 @@
|
||||
import { Suspense } from "react";
|
||||
import BlogsPage from "@/components/blogs";
|
||||
import Layout from "@/components/layout/StLayout";
|
||||
import { getStateInfo } from "@/lib/getStateInfo";
|
||||
@@ -33,7 +34,11 @@ export default async function Blogs() {
|
||||
// تشخیص شهر سمت سرور انجام میشود (همان الگوی app/specialties/page.js).
|
||||
return (
|
||||
<Layout name="/blogs">
|
||||
<BlogsPage cityId={isRoot ? null : (matchedCity?.id ?? null)} />
|
||||
{/* BlogsPage تگ انتخابی را از useSearchParams میخواند؛ Next 15 برای آن
|
||||
مرز Suspense لازم دارد وگرنه prerender صفحه میشکند. */}
|
||||
<Suspense fallback={null}>
|
||||
<BlogsPage cityId={isRoot ? null : (matchedCity?.id ?? null)} />
|
||||
</Suspense>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user