Files
nobat724_front/app/doctors/page.js
T
hamed b494473e40 feat: رفع لینک /doctor/undefined و متمایزسازی صفحهٔ اصلی شهرها
- ایجاد پرامپت برای رفع خطای 404 ناشی از لینک `/doctor/undefined` در سایت‌های بهبهان و یاسوج.
- افزودن کامپوننت `CityHighlights` برای نمایش محتوای یکتا و آمار پزشکان و تخصص‌های پرمراجعه در صفحهٔ اصلی هر شهر.
- به‌روزرسانی تست‌های مربوط به کامپوننت‌های `ItemDoctor` و `CityHighlights` برای اطمینان از عدم وجود لینک‌های نامعتبر و نمایش صحیح اطلاعات.
- ایجاد سند جدید برای مستندسازی خطاهای Search Console که باگ نیستند و نیاز به رفع ندارند.
- افزودن تست‌های واحد برای توابع `buildCityIntro` و دیگر توابع مرتبط با تولید متن یکتا برای صفحات لیست.
2026-08-08 19:14:50 +03:30

93 lines
4.1 KiB
JavaScript

import DoctorsPage from "@/components/doctors";
import Layout from "@/components/layout/StLayout";
import { buildDoctorParams } from "@/helper";
import { getStateInfo } from "@/lib/getStateInfo";
import { fetchReq } from "@/lib/req";
import { isNextRedirectError } from "@/lib/maintenance";
import { listingRobots } from "@/lib/listingRobots";
import { buildDoctorsIntro } from "@/lib/listingIntro";
import { resolveCityDisplayName } from "@/lib/domainHelpers";
export async function generateMetadata({ searchParams }) {
const awaitedParams = await searchParams;
const { matchedCity, matchedState, isRoot, repContext } = await getStateInfo();
const siteName = matchedCity?.site_name || repContext?.full_name || "نوبت 724";
// روی دامنهٔ ریشه نام رکورد («نوبت 724») برند است نه شهر — نباید در Title بنشیند.
const cityName = isRoot ? "" : matchedCity?.name || matchedState?.name || "";
const title = cityName
? `پزشکان ${cityName} | جستجو و رزرو نوبت | ${siteName}`
: `جستجوی پزشک و رزرو نوبت آنلاین | ${siteName}`;
const description = cityName
? `لیست پزشکان متخصص در ${cityName}. جستجو بر اساس تخصص و منطقه. رزرو آنلاین نوبت پزشکی در ${cityName}.`
: `جستجوی پزشکان متخصص در سراسر کشور. رزرو آنلاین نوبت پزشکی سریع و آسان با نوبت 724.`;
const image = "/assets/images/og-image.png";
return {
title,
description,
...listingRobots(awaitedParams),
openGraph: { title, description, images: [image] },
twitter: { card: "summary_large_image", title, description, images: [image] },
};
}
async function Doctors({ searchParams }) {
const awaitedSearchParams = await searchParams;
const { matchedCity, matchedState, isRoot, repContext, host } = await getStateInfo();
const API_URL = process.env.NEXT_PUBLIC_API_URL;
let doctors = null;
const stateParams = awaitedSearchParams.state;
const cityParams = awaitedSearchParams.city;
try {
// کپی — awaitedSearchParams همان شیئی است که generateMetadata هم می‌خواند؛
// تزریق city/state روی خودش، سیاست robots را به noindex می‌برد.
const newSearchParams = { ...awaitedSearchParams };
// Conditional State — روی دامنهٔ ریشه (nobat724) فیلتر اعمال نمی‌شود.
if (stateParams) newSearchParams.state = stateParams;
else if (matchedState && !isRoot) newSearchParams.state = matchedState.name;
// Conditional City
if (cityParams) newSearchParams.city = cityParams;
else if (matchedCity && !isRoot) newSearchParams.city = matchedCity.name;
const params = buildDoctorParams(newSearchParams);
// دامنه‌ی نماینده سراسری: backend فقط پزشکان همان نماینده را برمی‌گرداند؛ فیلتر شهر بی‌معنی است.
if (repContext?.is_global) {
delete params.city_id;
delete params.state_id;
params.domain = host;
}
doctors = await fetchReq(`${API_URL}/api/v1/doctors`, {
params,
});
} catch (error) {
// ریدایرکت نکست با throw کار می‌کند؛ بدون این rethrow، ریدایرکت حالت تعمیرات بلعیده می‌شود.
if (isNextRedirectError(error)) throw error;
console.error("Error fetching doctors:", error);
}
return (
<Layout>
<DoctorsPage
list={doctors}
params={searchParams}
matchedCity={matchedCity}
matchedState={matchedState}
// متن مقدمه سمت سرور ساخته می‌شود تا در HTML اولیه باشد و خزنده ببیندش.
// buildDoctorsIntro از قبل نوشته و تست شده بود اما هیچ‌جا رندر نمی‌شد، پس
// این صفحه روی هر ۳۵ دامنه محتوای یکسان داشت.
intro={buildDoctorsIntro(
resolveCityDisplayName(isRoot ? null : matchedCity),
doctors?.meta?.totalRecords
)}
/>
</Layout>
);
}
export default Doctors;