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

143 lines
9.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# صفحه عمومی پزشک — رفتار الزامی برای پروفایل بدون صاحب (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` جعلی است:
```jsx
<span>امتیاز: {doctor?.point}</span>
...
<span>{doctor?.satisfaction}% رضایت کاربران</span>
```
تیک تأیید (`Title.js` L5558) — **بدون هیچ شرطی** برای همه‌ی پزشکان:
```jsx
<TickCircle className="hidden lg:flex ..." />
<span>کد نظام پزشکی: {doctor?.medical_system_code}</span>
```
JSON-LD AggregateRating (`page.js` L123130) — فقط با `point > 0` گیت شده:
```jsx
...(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` می‌کند:
```jsx
{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) هم وقتی نظر واقعی نیست خالی/حذف است. در صورت نیاز شرط را صریح‌تر کن:
```jsx
...(!isUnclaimed && Number(doctor.point) > 0 && { aggregateRating: {...} }),
```
### ۳. تیک تأیید فقط برای claimed
در `Title.js` L5558، `TickCircle` را مشروط کن:
```jsx
{!isUnclaimed && <TickCircle className="hidden lg:flex ..." />}
```
کد نظام پزشکی بماند، فقط علامت ✓ سبز برای غیر-claimed حذف شود.
### ۴. noindex روی صفحه‌ی unclaimed
در `generateMetadata` (`page.js` L39–80) وقتی پروفایل claimed نیست، robots را noindex کن:
```jsx
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 را وارد کن:
```js
.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).