Files
nobat724_front/.claude/prompt/unclaimed-doctor-public-page.md
T

9.2 KiB
Raw Blame History

صفحه عمومی پزشک — رفتار الزامی برای پروفایل بدون صاحب (unclaimed)

پروژه

nobat724_front (سایت عمومی) — cross-repo. پرامپت همتای backend: clinicpro/.claude/prompt/unclaimed-doctor-null-rating.md آن پرامپت backend را اول اجرا کن؛ بعد این. backend فیلد point/satisfaction را برای پروفایل غیر-claimed به null برمی‌گرداند و owner_status را در پاسخ می‌گذارد.

زمینه

پزشکانِ ایمپورت‌شده از نظام پزشکی با owner_status = "unclaimed" می‌آیند: بدون عکس واقعی، بدون بیوگرافی، بدون آدرس/تلفن واقعی، صفر نظر. اما صفحه‌ی عمومی همان UI پزشک عادی را نشان می‌دهد — امتیاز پیش‌فرض جعلی، تیک تأیید، دکمه‌ی نوبت‌دهی مرده، ایندکس‌شدنِ صفحه‌ی نازک. این هم گمراه‌کننده است، هم برای سئو مضر (near-duplicate + thin content).

هدف: وقتی doctor.owner_status !== "claimed" مجموعه‌ای از اصلاحات الزامی اعمال شود؛ و به‌محض claimed شدن، صفحه دقیقاً به حالت عادی برگردد (همه‌ی این شرط‌ها فقط با یک flag گیت می‌شوند، پس خودکار برمی‌گردند).

شرط واحد در همه‌ی وظایف: const isUnclaimed = doctor?.owner_status !== "claimed"; (یعنی unclaimed یا pending_transfer). پروفایل موجودِ عادی owner_status = "claimed" دارد → هیچ تغییری نمی‌بیند.

فایل‌های مرتبط

فایل نقش
app/doctor/[slug]/page.js Server Component؛ fetch (getDoctor L1322)، generateMetadata L3980، JSON-LD aggregateRating L109161
components/doctor/detailDoctor/Title.js نمایش امتیاز/رضایت (L69–78)، تیک تأیید TickCircle (L5558)
components/doctor/claim/index.js بخش claim موجود (بنر «این پروفایل بر اساس اطلاعات عمومی...»، گیت با owner_status !== "unclaimed" L44)
components/doctor/detailDoctor/cards/comments/index.js بلوک نظرات
app/sitemap.js getDoctorUrls L98–112 — همه‌ی پزشکان بدون فیلتر
app/robots.js robots سراسری

وضعیت فعلی

نمایش امتیاز (Title.js L69–78) — همیشه، حتی وقتی point جعلی است:

<span>امتیاز: {doctor?.point}</span>
...
<span>{doctor?.satisfaction}% رضایت کاربران</span>

تیک تأیید (Title.js L5558) — بدون هیچ شرطی برای همه‌ی پزشکان:

<TickCircle className="hidden lg:flex ..." />
<span>کد نظام پزشکی: {doctor?.medical_system_code}</span>

JSON-LD AggregateRating (page.js L123130) — فقط با point > 0 گیت شده:

...(Number(doctor.point) > 0 && {
  aggregateRating: {
    "@type": "AggregateRating",
    ratingValue: doctor.point,
    bestRating: "5",
    ratingCount: comments?.length || 1,
  },
}),

generateMetadata (L3980) — هیچ robots/noindex ندارد. sitemap.js getDoctorUrls (L98–112) — برای هر پزشکِ دارای uuid یک /doctor/${d.uuid} می‌سازد، بدون فیلتر claim.

وظایف

۱. مخفی‌کردن امتیاز/رضایت برای unclaimed (مهم‌ترین)

در Title.js، بلوک امتیاز و رضایت (L69–78) فقط وقتی نمایش داده شود که امتیاز واقعی وجود دارد. چون backend اکنون point/satisfaction را null می‌کند:

{Number(doctor?.point) > 0 && (
  // بلوک «امتیاز: ...» و «...% رضایت کاربران»
)}

پس برای unclaimed (که point === null) اصلاً رندر نمی‌شود. مطمئن شو placeholder «بدون نمره»/«۰ رضایت» جایگزین نمی‌شود — کل بلوک حذف شود.

۲. حذف JSON-LD AggregateRating برای unclaimed

با null شدن point در backend، شرط موجود Number(doctor.point) > 0 (page.js L123) خودش aggregateRating را حذف می‌کند. تأیید کن که با point = null این شرط false می‌شود و بلوک review[] (L131–138) هم وقتی نظر واقعی نیست خالی/حذف است. در صورت نیاز شرط را صریح‌تر کن:

...(!isUnclaimed && Number(doctor.point) > 0 && { aggregateRating: {...} }),

۳. تیک تأیید فقط برای claimed

در Title.js L5558، TickCircle را مشروط کن:

{!isUnclaimed && <TickCircle className="hidden lg:flex ..." />}

کد نظام پزشکی بماند، فقط علامت ✓ سبز برای غیر-claimed حذف شود.

۴. noindex روی صفحه‌ی unclaimed

در generateMetadata (page.js L39–80) وقتی پروفایل claimed نیست، robots را noindex کن:

const isUnclaimed = doctor?.owner_status !== "claimed";
return {
  // ... title/description/openGraph موجود
  robots: isUnclaimed
    ? { index: false, follow: true }
    : undefined,
};

(follow: true تا لینک‌های داخلی دنبال شوند ولی خود صفحه‌ی نازک ایندکس نشود.)

۵. خارج‌کردن unclaimed از sitemap

در app/sitemap.js getDoctorUrls (L98–112)، فقط پزشکان claimed را وارد کن:

.filter((d) => d?.uuid && d?.owner_status === "claimed")
.map((d) => ({ url: `${base}/doctor/${d.uuid}`, ... }))

مطمئن شو پاسخ list (/api/v1/doctors, toListArray) فیلد owner_status دارد (طبق پرامپت backend دارد).

۶. برچسب «تأییدنشده» + CTA تصاحب

بخش claim موجود است (components/doctor/claim/index.js, mount در components/doctor/index.js L45) و با owner_status === "unclaimed" نمایش داده می‌شود. کارها:

  • یک برچسب کوچک «تأییدنشده» نزدیک نام/تیتر در Title.js برای isUnclaimed اضافه کن (متنی خنثی، مثلاً badge خاکستری «پروفایل تأییدنشده»).
  • بنر claim فعلی گیتش !== "unclaimed" است؛ اگر pending_transfer هم باید CTA ببیند، شرط را به owner_status !== "claimed" گسترش بده (هماهنگ با isUnclaimed). در غیر این‌صورت بدون تغییر بماند.

۷. مخفی‌کردن بلوک‌های خالی در حالت unclaimed

  • بلوک نظرات (cards/comments/index.js): وقتی isUnclaimed و صفر نظر واقعی، به‌جای «۰ از ۵ / بدون نمره» boilerplate، یا کل بلوک را مخفی کن یا یک متن زمینه‌ای «هنوز نظری ثبت نشده» نشان بده. تکرارِ پنج‌بار «بدون نمره» حذف شود.
  • بلوک موقعیت مکانی (cards/locations/index.js) already وقتی آدرس واقعی نیست null برمی‌گرداند (L5) — نیازی به تغییر نیست، فقط تأیید کن آدرسِ خودکار «مطب دکتر ...» بدون مختصات، map/تلفن جعلی نشان نمی‌دهد.

۸. (اختیاری، اولویت پایین) اسلاگ کلیدواژه‌دار

فعلاً slug === uuid (page.js L15, L113؛ sitemap L105؛ CTA ItemAppointment.js L57). تغییر به اسلاگ خوانا (مثل /doctor/سیده-شهلا-حسینی-پاتولوژی-یاسوج) تغییر بزرگ و ریسک‌دار است (باید redirect uuid→slug، تغییر canonical، هماهنگی با backend برای resolve اسلاگ). در این پرامپت پیاده نکن — فقط به‌عنوان کار جدا یادداشت کن. اگر انجام شد، canonical و sitemap و لینک‌های داخلی همه باید هماهنگ شوند و uuid همچنان resolve بماند.

نکات مهم

  • پاسخ API سه‌لایه است: getDoctor با json.data.data می‌خواند (page.js L21). فیلد owner_status روی همین object داخلی است.
  • همه‌ی شرط‌ها با یک flag: owner_status !== "claimed". به‌محض claim شدن (backend → 'claimed'), همه‌ی این تغییرات خودکار غیرفعال و صفحه عادی می‌شود. هیچ منطق «reset» جدا لازم نیست.
  • App Router: generateMetadata باید params را await کند (الگوی پروژه).
  • دکمه‌ی نوبت‌دهی («نوبت‌دهی غیرفعال») با doctor.active/free_turn گیت می‌شود، نه با claim. پزشکان ایمپورت‌شده در backend active_doctor_appointment = true دارند ولی slot واقعی ندارند → همان «غیرفعال» درست است. تغییرش نده مگر کاربر بخواهد.
  • استایل: MUI v5 + Tailwind، RTL، Vazir. badge «تأییدنشده» را با همان توکن‌های رنگی خنثی پروژه بساز.
  • بعد از تغییر: npm run build برای صحت (بدون خطای type/lint).