Files
nobat724_front/.claude/prompt/doctor-map-claim-modal.md
hamedandClaude Opus 4.8 4fed9a8c57 feat(doctor): fix map render + claim modal captcha/mobile + owner delete
- Doctor page map: the visible locations card read doctor.address (empty
  from the detail endpoint) while coordinates live in the separately
  fetched addresses. Thread `addresses` (with map.latitude/longitude) down
  page → DoctorPage → DetailDoctor → Locations; card hidden when empty
- Claim modal: updated info-box text ("نوبت‌های این پروفایل عمومی و غیرخاص
  هستند")، added mobile field (validated, must match account), added ALTCHA
  widget (submit disabled until captcha resolves; payload sent as `altcha`)
- Owner delete: services.deleteDoctor + a guarded two-step "حذف این پروفایل"
  in the claim success screen (owner enforced server-side; 403/409 shown)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 15:04:28 +03:30

8.6 KiB

رفع نمایش نقشه صفحه پزشک + بهبود مودال claim (کپچا/موبایل) + حذف پروفایل توسط مالک

پروژه

nobat724_front (سایت عمومی)

پرامپت همتا (backend اول): clinicpro/.claude/prompt/doctor-map-claim-captcha-delete.md — کپچا و فیلد mobile روی endpoint claim، و اجازهٔ حذف پروفایل به مالک. این پرامپت آن قرارداد را مصرف می‌کند.

زمینه

سه موضوع در صفحهٔ پزشک سایت:

  1. نقشه در صفحهٔ پزشک درست نمایش داده نمی‌شود (مثال: /doctor/ab747d75-2114-42b8-9e6d-abdaa338edbe).
  2. مودال «تأیید و مدیریت پروفایل» (claim) از قبل هست ولی طبق سناریو باید متن کادر اطلاع‌رسانی به‌روز شود، فیلد موبایل و کپچای ALTCHA اضافه شود.
  3. پس از مالک‌شدن، پزشک باید دکمهٔ حذف پروفایل داشته باشد.

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

فایل نقش
app/doctor/[slug]/page.js فچ doctor + getDoctorAddresses(doctor.id) (خط ۲۴، ۱۰۵)
components/doctor/detailDoctor/cards/locations/index.js کارت آدرس‌ها — از doctor?.address می‌خواند (خط ۱۱)
components/doctor/detailDoctor/cards/locations/Item.js رندر iframe گوگل با data.map.latitude/longitude (خط ۴۶، ۶۵)
components/doctor/claim/index.js ClaimProfileSection موجود (سکشن + مودال)
components/Altcha.js کامپوننت ALTCHA موجود سایت
services/response.js getDoctorClaimInfo / postDoctorClaim موجود

وظیفه ۱ — رفع نمایش نقشه صفحه پزشک

ریشه (تأییدشده)

کارت آدرس‌ها از doctor?.address می‌خواند:

// components/doctor/detailDoctor/cards/locations/index.js:11
{doctor?.address?.map((item, idx) => ( <Item data={item} /> ))}

اما مختصات نقشه در پاسخِ جداگانه‌ی GET /api/v1/clinic-pro/doctor-addresses/{id} است (backend آن را با شکل map: { latitude, longitude } برمی‌گرداند). در صفحه، این پاسخ در متغیر addresses فچ می‌شود و فقط در JSON-LD استفاده شده (app/doctor/[slug]/page.js:105,141)، ولی به کارت دیداری آدرس‌ها پاس داده نمی‌شود. پس doctor.address یا خالی است یا map ندارد → Item شرط data?.map?.latitude را رد می‌کند → نقشه هرگز نمایش داده نمی‌شود.

راه‌حل

addresses (که map.latitude/longitude دارد) را به همان کارتی که نقشه را رندر می‌کند برسان:

  • در app/doctor/[slug]/page.js، addresses را به DoctorPage/detailDoctor پاس بده (prop جدید یا ادغام در doctor.address).
  • در locations/index.js، به‌جای doctor?.address از همان آرایهٔ addresses استفاده کن که هر آیتم map: { latitude, longitude } و address/telephone دارد.
  • شکل مصرفی Item (data.map.latitude, data.map.longitude, data.address, data.telephone) را با شکل خروجی doctor-addresses هم‌تراز کن (backend همین کلیدها را می‌دهد — تأیید در clinicpro/src/Doctor/Controller/DoctorController.php:166).
// locations/index.js — نمونه
export default function Locations({ addresses }) {
  if (!addresses?.length) return null;
  return (<>{addresses.map((item, idx) => <Item key={idx} data={item} />)}</>);
}
  • edge: پزشکِ ایمپورت‌شده (مثل نمونهٔ کاربر) آدرس ندارد → آرایه خالی → کارت اصلاً رندر نشود (نه نقشهٔ خراب). این درست است.
  • edge: آدرسِ بدون مختصات (latitude=null) → دکمهٔ مسیریابی/iframe نمایش داده نشود، ولی خود آدرس/تلفن نمایش داده شود.

وظیفه ۲ — بهبود مودال claim (متن، موبایل، کپچا)

components/doctor/claim/index.js از قبل سکشن + مودال دارد. تغییرات:

الف) متن کادر اطلاع‌رسانی (طبق سناریو):

«این پروفایل بر اساس اطلاعات عمومی سازمان نظام پزشکی ایجاد شده است و هنوز توسط پزشک تأیید و مدیریت نمی‌شود. نوبت‌های این پروفایل عمومی و غیرخاص هستند. آیا شما این پزشک هستید؟» + دکمهٔ «تأیید و مدیریت این پروفایل».

ب) فیلد موبایل در فرم: علاوه بر نام/نام‌خانوادگی/کد ملی/تاریخ تولد، فیلد شماره موبایل اضافه شود (پیش‌پرشده از کاربر لاگین‌شده اگر در دسترس است). به بدنهٔ postDoctorClaim اضافه شود:

await request.postDoctorClaim(doctor.uuid, {
  national_code, birth_date, first_name, last_name, mobile, altcha, // ← mobile و altcha جدید
});

ج) کپچای ALTCHA: از کامپوننت موجود components/Altcha.js استفاده کن. تا وقتی کاربر کپچا را حل نکرده، دکمهٔ ارسال غیرفعال بماند:

import Altcha from "@/components/Altcha";
// ...
const [altcha, setAltcha] = useState("");
// در فرم:
<Altcha onVerified={setAltcha} />
<Button disabled={loading || !altcha} onClick={submit}>تأیید هویت و تصاحب پروفایل</Button>
  • payload کپچا را در بدنهٔ claim بفرست (backend همتا CaptchaGuard::assertValid را چک می‌کند). نام فیلد را با آنچه CaptchaGuard انتظار دارد هماهنگ کن (بررسی AuthController سایت/بک‌اند — معمولاً altcha).
  • اگر ALTCHA_ENABLED=false (dev) بک‌اند کپچا را نادیده می‌گیرد؛ ولی UI کپچا را نشان بده تا در prod کار کند.

stateهای موجود مودال (loading/error/success/double-submit/پیام خوش‌آمد) حفظ شوند؛ فقط فیلدها و کپچا اضافه می‌شوند. خطای ERR_CAPTCHA_001 از بک‌اند → پیام «تأیید امنیتی ناموفق بود، دوباره تلاش کنید».

وظیفه ۳ — حذف پروفایل توسط مالک

پس از claim موفق (پزشک مالک شد)، در صفحهٔ مدیریت پروفایل پزشک (یا همان صفحهٔ پزشک وقتی کاربرِ لاگین‌شده مالک است) دکمهٔ «حذف پروفایل» نمایش داده شود.

  • فقط وقتی نمایش داده شود که کاربرِ لاگین‌شده مالکِ claimed این پروفایل باشد (از owner_status + تطبیق کاربر). مرجع نهایی مجوز، backend است.
  • کلیک → دیالوگ تأیید با پیام هشدار (طبق سناریو: «قبل از حذف، پیام هشدار نمایش داده شود»)، سپس:
// متد جدید در services/response.js
deleteDoctor: (uuid) => api.delete(`api/v1/doctor/${uuid}`, { requireAuth: true }),
  • backend اجازهٔ حذف مالک را می‌دهد (پرامپت همتا). خطاها: ۴۰۳ (مالک نیست)، ۴۰۹ (پزشک نوبت ثبت‌شده دارد) → پیام فارسی مناسب.
  • پس از حذف موفق → هدایت به صفحهٔ اصلی/پنل + toast موفقیت.

نکات مهم

  • backend اول اجرا شود (کپچا + فیلد mobile + delete مالک) وگرنه این تغییرات ۴۲۲/۴۰۳ می‌گیرند.
  • multi-domain: مودال claim کامپوننت مشترک است و روی همهٔ دامنه‌ها/زیردامنه‌ها کار می‌کند؛ منطق را per-domain تکرار نکن.
  • RTL، فارسی، Vazir، date-picker شمسی موجود؛ کتابخانهٔ جدید اضافه نکن.
  • هیچ درخواستی از فرانت به API سازمان (شاهکار/ثبت‌احوال) نرود؛ همه backend.
  • تست:
    cd nobat724_front && npm run lint && npm run build
    # صفحهٔ پزشکِ دارای آدرس با مختصات → نقشه نمایش داده شود؛
    # پزشک ایمپورت‌شدهٔ بدون آدرس → کارت نقشه رندر نشود (نه خراب)؛
    # مودال claim → کپچا اجباری، فیلد موبایل، ارسال موفق؛ دکمهٔ حذف فقط برای مالک.