Connect the existing doctor-page review UI to the rebuilt backend contract
(see clinicpro feat/rating-multidimensional).
- page.js fetches the rating aggregate alongside comments and passes
rateAggregate down to the chart (point / satisfaction / 5 dimensions).
- Submit form sends the five dimensions with doctor_uuid; comment/reply
send {doctor_uuid, comment, parent}; 401/403 ERR_RATING_NOT_ELIGIBLE
surface friendly guidance.
- Like/dislike call POST /like/{uuid} with value and update from the
response; replies render nested.
- ModalAnswer gates the submit button on GET /rate/{uuid}/eligibility.
- services/response.js: getRateEligibility, postCommentsLike(uuid, value),
drop unused patchDoctorRate. Fix hardcoded modal title; empty-state for
no comments. Remove orphaned AnswerField.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9.2 KiB
اتصال UI غنیِ نظرات/امتیاز صفحه پزشک به قرارداد جدید بکاند
پروژه
nobat724_front (سایت عمومی).
Cross-repo: این پرامپت به قرارداد غنیِ بکاند وابسته است که در
clinicpro/.claude/prompt/rating-multidimensional-and-rich-comments.mdتعریف شده. آن پرامپت بکاند باید اول اجرا شود.
زمینه
UI غنیِ نظرات/امتیاز صفحه پزشک (چارت ۵بُعدی + دایرهی رضایت + ستارهی کلی + نظرات با نام/عکس/لایک/دیسلایک/پاسخ) از قبل در کد وجود دارد و باید حفظ شود. این کامپوننتها فیلدهایی میخوانند که بکاندِ سادهی قبلی نمیداد؛ حالا بکاند قرارداد غنی را تأمین میکند و فرانت فقط باید درست به آن وصل شود (و چند نقص جزئی رفع شود).
هشدار: یک تلاش قبلی این UI را بهاشتباه «سادهسازی» کرد و سپس به HEAD برگردانده شد. UI غنی نباید ساده شود؛ فقط به قرارداد جدید وصل شود.
قرارداد بکاند (مرجع — از پرامپت همتا)
GET /api/v1/rate/{uuid}(عمومی) →{ data: { point, satisfaction, averages:[{name,label,progress}] } }POST /api/v1/rate(AUTH) ←{ doctor_uuid, waiting_time_at_clinic, accuracy_of_diagnosis, doctor_behavior, clinic_cleanliness, doctor_expertise }(هر کدام ۰–۱۰۰)GET /api/v1/comments/{uuid}(عمومی) →{ data: { data: [ { uuid, comment, created, parent, author:{real_name,picture:[{url}]}, like_status:{like_count,dislike_count,current_user_like:{like,dislike}}, replies:[…] } ] } }POST /api/v1/comment(AUTH) ←{ doctor_uuid, comment, parent }POST /api/v1/like/{commentUuid}(AUTH) ←{ value: 1|-1 }→{ data: { like_count, dislike_count, current_user_like } }GET /api/v1/rate/{uuid}/eligibility(AUTH) →{ data: { eligible: bool } }(قبلاً اضافه شده)- گارد بکاند: ثبت نظر/امتیاز فقط برای کاربرِ دارای نوبت confirmed در ۳۰ روز گذشته → در غیر این صورت
403 ERR_RATING_NOT_ELIGIBLE.
فایلهای مرتبط (UI موجود که باید وصل شود)
| فایل | نقش | اصلاح موردنیاز |
|---|---|---|
app/doctor/[slug]/page.js |
fetch server-side | علاوه بر comments، تجمیع امتیاز از GET /rate/{uuid} را fetch کن و بهصورت prop بده |
components/doctor/index.js → detailDoctor/index.js → cards/comments/index.js |
زنجیره prop | rateAggregate (point/satisfaction/averages) را عبور بده |
cards/comments/chart/index.js |
چارت ۵بُعدی + ستاره | بهجای doctor.point/satisfaction/average_rate.averages از prop rateAggregate بخوان |
cards/comments/chart/ProgressAll.js |
دایرهی رضایت | satisfaction را از rateAggregate بگیرد |
cards/comments/modal/ModalAnswer.js |
مودال ثبت | پیشبارگذاری امتیاز قبلی را حذف/سازگار کن؛ eligibility بررسی شود |
cards/comments/modal/content/index.js |
هدر مودال | عنوان hardcoded «دکتر مهران امینی» → doctor?.name |
cards/comments/modal/content/Form.js |
ثبت امتیاز+نظر | body امتیاز را با doctor_uuid بفرست (نه doctor_id)؛ مدیریت ۴۰۳/۴۰۱ |
app/component/comment/ItemUser.js |
رندر نظر | data.created→تاریخ، author/like_status/replies حفظ؛ لایک/دیسلایک به postCommentsLike(uuid, value) وصل شود |
app/component/comment/SendAnswer.js |
ثبت پاسخ | body را با doctor_uuid و parent بفرست |
services/response.js |
wrappers | postDoctorRate بدنهی چندبُعدی؛ postCommentsLike(uuid, value)؛ getRateEligibility (موجود) |
وظایف
۱. fetch تجمیع امتیاز در صفحه و عبور prop
در app/doctor/[slug]/page.js کنار fetch فعلیِ doctor/comments، تجمیع امتیاز را بگیر:
const resRate = await axiosInstance.get(`${API_URL}/api/v1/rate/${doctor.uuid}`);
const rateAggregate = resRate?.data?.data ?? { point: 0, satisfaction: 0, averages: [] };
سپس rateAggregate را از DoctorPage → DetailDoctor → Comments → Chart/ProgressAll عبور بده.
۲. اتصال چارت به قرارداد جدید (chart/index.js + ProgressAll.js)
Chartبهجایdoctor?.average_rate?.averagesازrateAggregate.averagesاستفاده کند (همان شکل{name,label,progress}کهProgressDetailانتظار دارد).امتیاز کلی کاربران: {rateAggregate.point} از ۵و ستارهvalue={rateAggregate.point}.{rateAggregate.satisfaction}% رضایت کاربرانوProgressAllباsatisfaction={rateAggregate.satisfaction}.- ساختار/استایل و کامپوننتهای
ProgressDetail/ProgressAll/loadingها حفظ شوند.
۳. فرم ثبت امتیاز چندبُعدی (Form.js + ModalAnswer.js + content/index.js)
- ساختار ۵بُعدیِ موجود (
Rates+ProgressChange+comment.rate) حفظ شود. - body امتیاز با
doctor_uuidارسال شود (نهdoctor_id):(ترتیب ایندکسها را باrequest.postDoctorRate({ doctor_uuid: doctor.uuid, waiting_time_at_clinic: rate[0].progress, accuracy_of_diagnosis: rate[1].progress, doctor_behavior: rate[2].progress, clinic_cleanliness: rate[3].progress, doctor_expertise: rate[4].progress, });progress_detail.jsonتطبیق بده — همان مپ فعلیِ Form.) - نظر با
doctor_uuidارسال شود:postDoctorComment({ doctor_uuid: doctor.uuid, comment, parent: null }). - چون بکاند upsert است،
patchDoctorRate/defaultRateلازم نیست؛ همیشهpostDoctorRate. - عنوان مودال hardcoded →
doctor?.name. - در
ModalAnswer، اگر pre-load امتیاز قبلی مشکلساز است حذفش کن (بکاند endpoint «امتیاز کاربر فعلی» ندارد؛getDoctorRateحالا تجمیع برمیگرداند نه رأی کاربر) — فرم با مقدار اولیهی صفر باز شود. - مدیریت خطا: ۴۰۱ → «برای ثبت باید وارد شوید»؛ ۴۰۳ با
ERR_RATING_NOT_ELIGIBLE→ پیام واجد بودن؛ سایر → خطای عمومی. (باalert)
۴. لایک/دیسلایک واقعی (ItemUser.js + services/response.js)
postCommentsLikeرا به(commentUuid, value)تغییر بده:api.post(api/v1/like/${uuid}, { value }, { requireAuth: true }).- در
ItemUser، هنگام کلیک لایکvalue=1و دیسلایکvalue=-1بفرست؛ از پاسخ{like_count,dislike_count,current_user_like}state محلی را بهروز کن (بهجای دستکاری دستیِ فعلی رویdata.like_status). - نمایش author/created/replies حفظ شود.
۵. ثبت پاسخ (SendAnswer.js)
- body را
{ doctor_uuid: doctor.uuid /* یا از data */, comment, parent: data.uuid }کن (نهdoctor_id). - منبع
doctor.uuidرا در این کامپوننت بررسی کن؛ اگر در دسترس نیست، از prop عبور بده.
۶. wrapperها (services/response.js)
postDoctorRate(data)→POST api/v1/rateباrequireAuth(بدنهی چندبُعدی؛ بدون تغییر امضا، فقط دیتای جدید).getRateEligibility(uuid)(موجود) حفظ شود.getDoctorRate(uuid)حالا تجمیع برمیگرداند — مصرفکنندهها (chart) بایدdata.point/...بخوانند.
نکات مهم
- UI غنی حفظ شود؛ ساده نکن. فقط به قرارداد جدید وصل کن و نقصهای جزئی (hardcode،
doctor_id→doctor_uuid، toggle لایک) را رفع کن. app/component/comment/*بین صفحه پزشک و بلاگ مشترک است، اما فراخوانی آن در بلاگ (components/blog/detail/index.js) کامنتاوت است؛ پس تغییرات صفحه پزشک بلاگ را نمیشکند — ولی هنگام تغییرItemUser/Commentاین اشتراک را در نظر بگیر و چیزی که build بلاگ را بشکند وارد نکن.current_user_likeدر لیست عمومی همیشه false است (طبق تصمیم بکاند)؛ وضعیت واقعی پس از کلیک از پاسخtoggleLikeمیآید — UI را optimistic نگهدار.- نظرات نیازمند تأیید ادمیناند؛ لیست لوکال ممکن است خالی باشد → empty-state، نه خطا.
- App Router + RTL + Vazir؛ کتابخانهی جدید اضافه نکن.
- بعد از تغییر:
npm run buildبدون خطا؛/doctor/[slug]و/blog/[slug]هر دو باید کامپایل شوند.