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>
150 lines
4.9 KiB
JavaScript
150 lines
4.9 KiB
JavaScript
"use client";
|
|
|
|
import Image from "next/image";
|
|
import { imageUrl } from "@/helper";
|
|
import { Button, CircularProgress } from "@mui/material";
|
|
|
|
// Icons
|
|
import BoldDisLikeComment from "@/components/icons/BoldDisLikeComment";
|
|
import BoldLikeComment from "@/components/icons/BoldLikeComment";
|
|
import DisLikeComment from "@/components/icons/DisLikeComment";
|
|
import LikeComment from "@/components/icons/LikeComment";
|
|
import Replies from "./Replies";
|
|
import SendAnswer from "./SendAnswer";
|
|
import { alert, isUserLoggedIn, timeAgo } from "@/helper";
|
|
import { request } from "@/services/response";
|
|
import { useState } from "react";
|
|
|
|
function ItemUser({ data, doctor, isReply = false }) {
|
|
const [loadingLike, setLoadingLike] = useState(false);
|
|
const [loadingDislike, setLoadingDislike] = useState(false);
|
|
const [openAnswer, setOpenAnswer] = useState(false);
|
|
const [status, setStatus] = useState({
|
|
like_count: data.like_status?.like_count || 0,
|
|
dislike_count: data.like_status?.dislike_count || 0,
|
|
like: data.like_status?.current_user_like?.like || false,
|
|
dislike: data.like_status?.current_user_like?.dislike || false,
|
|
});
|
|
|
|
const sendReq = async (type) => {
|
|
if (!isUserLoggedIn()) {
|
|
alert("warning", "برای ثبت نظر باید ابتدا وارد حساب کاربری شوید.");
|
|
return;
|
|
}
|
|
if (type === "like") setLoadingLike(true);
|
|
else setLoadingDislike(true);
|
|
|
|
try {
|
|
const res = await request.postCommentsLike(data.uuid, type === "like" ? 1 : -1);
|
|
const d = res.data;
|
|
setStatus({
|
|
like_count: d.like_count,
|
|
dislike_count: d.dislike_count,
|
|
like: d.current_user_like.like,
|
|
dislike: d.current_user_like.dislike,
|
|
});
|
|
} catch (err) {
|
|
alert("error", "خطایی رخ داد! لطفاً دوباره تلاش کنید.");
|
|
} finally {
|
|
if (type === "like") setLoadingLike(false);
|
|
else setLoadingDislike(false);
|
|
}
|
|
};
|
|
|
|
const toggleAnswer = () => {
|
|
if (isUserLoggedIn()) {
|
|
setOpenAnswer((prev) => !prev);
|
|
} else {
|
|
alert("warning", "برای ارسال پاسخ باید ابتدا وارد حساب کاربری شوید.");
|
|
}
|
|
};
|
|
|
|
return (
|
|
<li className="w-full">
|
|
<div className="flex items-center justify-start gap-2">
|
|
<Image
|
|
className="!w-[32px] !h-[32px]"
|
|
src={imageUrl(data.author?.picture?.[0]?.url)}
|
|
width={40}
|
|
height={40}
|
|
alt="profile"
|
|
/>
|
|
<div className="flex flex-col items-start justify-center gap-[8px]">
|
|
<p className="text-[#343A40] text-[14px] md:text-[16px] font-medium">
|
|
{data.author?.real_name}
|
|
</p>
|
|
<p className="text-[#7E7E7E] text-[12px] md:text-[14px] font-medium">
|
|
{timeAgo(data.created)}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className="mt-2">
|
|
<p className="text-[#495057] text-[12px] md:text-[14px]">{data.comment}</p>
|
|
|
|
<div className="flex items-center justify-start mt-2 gap-[28px]">
|
|
{/* لایک */}
|
|
<div className="flex items-center justify-start gap-1">
|
|
<div className="cursor-pointer" onClick={() => sendReq("like")}>
|
|
{loadingLike ? (
|
|
<CircularProgress
|
|
className="!w-[24px] !h-[24px]"
|
|
sx={{ color: "#7E7E7E" }}
|
|
/>
|
|
) : status.like ? (
|
|
<BoldLikeComment />
|
|
) : (
|
|
<LikeComment />
|
|
)}
|
|
</div>
|
|
<p className="text-[#6C757D] text-[12px] select-none">
|
|
{status.like_count || ""}
|
|
</p>
|
|
</div>
|
|
|
|
{/* دیسلایک */}
|
|
<div className="flex items-center justify-start gap-1">
|
|
<div className="cursor-pointer" onClick={() => sendReq("dislike")}>
|
|
{loadingDislike ? (
|
|
<CircularProgress
|
|
className="!w-[24px] !h-[24px]"
|
|
sx={{ color: "#7E7E7E" }}
|
|
/>
|
|
) : status.dislike ? (
|
|
<BoldDisLikeComment />
|
|
) : (
|
|
<DisLikeComment />
|
|
)}
|
|
</div>
|
|
<p className="text-[#6C757D] text-[12px] select-none">
|
|
{status.dislike_count || ""}
|
|
</p>
|
|
</div>
|
|
|
|
{!isReply && (
|
|
<Button
|
|
className="!text-[#495057] !text-[12px] !font-medium"
|
|
onClick={toggleAnswer}
|
|
variant="text"
|
|
>
|
|
پاسخ
|
|
</Button>
|
|
)}
|
|
</div>
|
|
|
|
{!isReply && openAnswer && (
|
|
<SendAnswer
|
|
doctor={doctor}
|
|
parentUuid={data.uuid}
|
|
onSent={() => setOpenAnswer(false)}
|
|
/>
|
|
)}
|
|
{!isReply && data.replies?.length > 0 && (
|
|
<Replies data={data} doctor={doctor} />
|
|
)}
|
|
</div>
|
|
</li>
|
|
);
|
|
}
|
|
|
|
export default ItemUser;
|