# اتصال 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، تجمیع امتیاز را بگیر: ```jsx 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`): ```jsx 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]` هر دو باید کامپایل شوند.