get data with filter location for first time, get comment of doctor and show in doctor page, handle new comment

This commit is contained in:
Ehsan
2025-09-03 22:05:26 +03:30
parent 6920e4078b
commit ebfe47468c
14 changed files with 187 additions and 50 deletions
+5 -1
View File
@@ -33,7 +33,11 @@ function ItemDoctor({ doctor }) {
</TextLoading> </TextLoading>
<TextLoading width={120} height={15}> <TextLoading width={120} height={15}>
<p className="text-[#616161] text-[14px] font-normal"> <p className="text-[#616161] text-[14px] font-normal">
تخصص: {doctor?.expertise} تخصص:
{doctor?.specialties?.map(
(item, idx) =>
`${item.name} ${doctor.specialties.length === idx + 1 ? "" : "|"} `
)}
</p> </p>
</TextLoading> </TextLoading>
<CustomLoading width={100} height={25}> <CustomLoading width={100} height={25}>
+7 -4
View File
@@ -1,7 +1,7 @@
import { Slider } from "@mui/material"; import { Slider } from "@mui/material";
import { useState } from "react"; import { useState } from "react";
function ProgressChange({ data }) { function ProgressChange({ data, changeData, parent }) {
const [value, setValue] = useState(70); const [value, setValue] = useState(70);
return ( return (
@@ -10,9 +10,12 @@ function ProgressChange({ data }) {
{data.label} {data.label}
</p> </p>
<Slider <Slider
defaultValue={value} defaultValue={data.progress}
className="!w-full lg:!w-[265px] !rounded-[10px] !h-[11px]" className="!w-full lg:!w-[265px] !rounded-[10px] !h-[11px]"
onChange={(e) => setValue(e.target.value)} onChange={(e) => {
setValue(e.target.value);
changeData(parent, data.name, e.target.value);
}}
sx={{ sx={{
"& .MuiSlider-rail": { "& .MuiSlider-rail": {
background: "#E8E8E8", background: "#E8E8E8",
@@ -32,7 +35,7 @@ function ProgressChange({ data }) {
}, },
}} }}
/> />
<p className="text-[#525252] text-[20px] font-medium w-[30px] min-w-[32px]"> <p className="text-[#525252] text-[20px] font-medium w-[30px] min-w-[32px] text-center">
{value} {value}
</p> </p>
</li> </li>
+27 -16
View File
@@ -13,10 +13,13 @@ function ItemUser({ update, setUpdate, isReply, list, setList, data }) {
const handleLike = (lengthLike, data, nameLike, nameIsLike) => { const handleLike = (lengthLike, data, nameLike, nameIsLike) => {
if (nameIsLike === "is_like" && data.is_dislike) { if (nameIsLike === "is_like" && data.is_dislike) {
data.is_dislike = false; data.is_dislike = false;
data.dislike--; data.like_status.dislike_count--;
} else if (nameIsLike === "is_dislike" && data.is_like) { } else if (
data.is_like = false; nameIsLike === "is_dislike" &&
data.like--; data.like_status.current_user_like.like
) {
data.like_status.current_user_like.like = false;
data.like_status.like_count--;
} }
data[nameLike] = lengthLike; data[nameLike] = lengthLike;
data[nameIsLike] = !data[nameIsLike]; data[nameIsLike] = !data[nameIsLike];
@@ -28,14 +31,14 @@ function ItemUser({ update, setUpdate, isReply, list, setList, data }) {
<div className="flex items-center justify-start gap-2"> <div className="flex items-center justify-start gap-2">
<Image <Image
className="!w-[32px] !h-[32px] sm:!w-[35px] sm:!h-[35px] md:!h-[37px] md:!w-[37px] lg:!w-[40px] lg:!h-[40px]" className="!w-[32px] !h-[32px] sm:!w-[35px] sm:!h-[35px] md:!h-[37px] md:!w-[37px] lg:!w-[40px] lg:!h-[40px]"
src={data.profile} src={data.author?.picture[0]?.url}
width={40} width={40}
height={40} height={40}
alt="profile" alt="profile"
/> />
<div className="flex flex-col items-start justify-center gap-[8px]"> <div className="flex flex-col items-start justify-center gap-[8px]">
<p className="text-[#343A40] text-[14px] md:text-[16px] font-medium"> <p className="text-[#343A40] text-[14px] md:text-[16px] font-medium">
{data.name} {data.author.real_name}
</p> </p>
<p className="text-[#7E7E7E] text-[12px] md:text-[14px] font-medium"> <p className="text-[#7E7E7E] text-[12px] md:text-[14px] font-medium">
3 روز پیش 3 روز پیش
@@ -52,17 +55,23 @@ function ItemUser({ update, setUpdate, isReply, list, setList, data }) {
className="cursor-pointer" className="cursor-pointer"
onClick={() => onClick={() =>
handleLike( handleLike(
data.is_like ? data.like - 1 : data.like + 1, data.like_status.current_user_like.like
? data.like_status.like_count - 1
: data.like_status.like_count + 1,
data, data,
"like", "like",
"is_like" "is_like"
) )
} }
> >
{data.is_like ? <BoldLikeComment /> : <LikeComment />} {data.like_status.current_user_like.like ? (
<BoldLikeComment />
) : (
<LikeComment />
)}
</div> </div>
<p className="text-[#6C757D] text-[12px] font-normal select-none"> <p className="text-[#6C757D] text-[12px] font-normal select-none">
{data.like} {data.like_status.like_count || ""}
</p> </p>
</div> </div>
<div className="flex items-center justify-start gap-1"> <div className="flex items-center justify-start gap-1">
@@ -70,7 +79,9 @@ function ItemUser({ update, setUpdate, isReply, list, setList, data }) {
className="cursor-pointer" className="cursor-pointer"
onClick={() => onClick={() =>
handleLike( handleLike(
data.is_dislike ? data.dislike - 1 : data.dislike + 1, data.is_dislike
? data.like_status.dislike_count - 1
: data.like_status.dislike_count + 1,
data, data,
"dislike", "dislike",
"is_dislike" "is_dislike"
@@ -80,7 +91,7 @@ function ItemUser({ update, setUpdate, isReply, list, setList, data }) {
{data.is_dislike ? <BoldDisLikeComment /> : <DisLikeComment />} {data.is_dislike ? <BoldDisLikeComment /> : <DisLikeComment />}
</div> </div>
<p className="text-[#6C757D] text-[12px] font-normal select-none"> <p className="text-[#6C757D] text-[12px] font-normal select-none">
{data.dislike} {data.like_status.dislike_count || ""}
</p> </p>
</div> </div>
<Button <Button
@@ -107,7 +118,7 @@ function ItemUser({ update, setUpdate, isReply, list, setList, data }) {
ارسال پاسخ ارسال پاسخ
</Button> </Button>
</div> </div>
{!!data.replays.length && ( {!!data.replies.length && (
<div className="flex items-center justify-start gap-1 my-2"> <div className="flex items-center justify-start gap-1 my-2">
<Button <Button
className="!text-[#0FA1B3] !text-[12px] !font-medium" className="!text-[#0FA1B3] !text-[12px] !font-medium"
@@ -116,7 +127,7 @@ function ItemUser({ update, setUpdate, isReply, list, setList, data }) {
setUpdate(!update); setUpdate(!update);
}} }}
> >
مشاهده پاسخ ها ({data.replays.length}) مشاهده پاسخ ها ({data.replies.length})
<div <div
className={`!mr-1 ${ className={`!mr-1 ${
data.is_open_reply ? "!rotate-0" : "!rotate-180" data.is_open_reply ? "!rotate-0" : "!rotate-180"
@@ -128,10 +139,10 @@ function ItemUser({ update, setUpdate, isReply, list, setList, data }) {
</div> </div>
)} )}
<ul <ul
className={`flex mr-12 flex-col justify-center transition-all duration-500 overflow-hidden items-start gap-12 w-full className={`flex pr-12 flex-col justify-center transition-all duration-500 overflow-hidden items-start gap-12 w-full
${data.is_open_reply ? "max-h-screen mt-12" : "max-h-0"}`} ${data.is_open_reply ? "max-h-fit mt-12" : "max-h-0"}`}
> >
{data.replays.map((item, idx) => ( {data.replies.map((item, idx) => (
<ItemUser <ItemUser
key={idx} key={idx}
data={item} data={item}
+4 -4
View File
@@ -2,7 +2,7 @@
import { useState } from "react"; import { useState } from "react";
import ItemUser from "./ItemUser"; import ItemUser from "./ItemUser";
function Comment({ data }) { function Comment({ data, comments }) {
const [list, setList] = useState(data?.comments); const [list, setList] = useState(data?.comments);
const [update, setUpdate] = useState(false); const [update, setUpdate] = useState(false);
@@ -12,9 +12,9 @@ function Comment({ data }) {
false && "w-full" false && "w-full"
}`} }`}
> >
{data && {comments &&
!!data?.comments?.length && comments.length &&
data?.comments.map((item, idx) => ( comments.map((item, idx) => (
<ItemUser <ItemUser
key={idx} key={idx}
list={list} list={list}
+8 -1
View File
@@ -5,6 +5,7 @@ import axios from "axios";
async function Doctor({ params: { slug } }) { async function Doctor({ params: { slug } }) {
let doctors = null; let doctors = null;
let doctor = null; let doctor = null;
let comments = null;
try { try {
const resDoctors = await axios.get( const resDoctors = await axios.get(
"https://back-dev.clinic-pro.ir/api/v1/doctors?active=1" "https://back-dev.clinic-pro.ir/api/v1/doctors?active=1"
@@ -15,6 +16,12 @@ async function Doctor({ params: { slug } }) {
doctor = resDoctor.data; doctor = resDoctor.data;
doctors = resDoctors.data.data; doctors = resDoctors.data.data;
if (doctor) {
const resComments = await axios.get(
`https://back-dev.clinic-pro.ir/api/v1/clinicpro/comments/${doctor.id}`
);
comments = resComments?.data?.data;
}
} catch (error) { } catch (error) {
// console.error("problem with req:", error.message); // console.error("problem with req:", error.message);
// return redirect("/unauthorized"); // return redirect("/unauthorized");
@@ -22,7 +29,7 @@ async function Doctor({ params: { slug } }) {
return ( return (
<Layout> <Layout>
<DoctorPage doctor={doctor} doctors={doctors} /> <DoctorPage doctor={doctor} doctors={doctors} comments={comments} />
</Layout> </Layout>
); );
} }
+8 -1
View File
@@ -8,8 +8,15 @@ async function Doctors({ searchParams }) {
let doctors = null; let doctors = null;
try { try {
const params = {};
if (matchedCity) params.city = matchedCity;
if (matchedState) params.state = matchedState;
const response = await axios.get( const response = await axios.get(
"https://back-dev.clinic-pro.ir/api/v1/doctors" "https://back-dev.clinic-pro.ir/api/v1/doctors",
{
// params,
}
); );
doctors = response.data.data; doctors = response.data.data;
} catch (error) { } catch (error) {
@@ -2,7 +2,7 @@ import Chart from "./chart";
import Comment from "@/app/component/comment"; import Comment from "@/app/component/comment";
import ModalAnswer from "./modal/ModalAnswer"; import ModalAnswer from "./modal/ModalAnswer";
function Comments({ doctor }) { function Comments({ doctor, comments }) {
return ( return (
<div className="mt-[24px] sm:mt-[27px] md:mt-[29px] lg:mt-[32px]"> <div className="mt-[24px] sm:mt-[27px] md:mt-[29px] lg:mt-[32px]">
<a <a
@@ -19,7 +19,7 @@ function Comments({ doctor }) {
<ModalAnswer doctor={doctor} /> <ModalAnswer doctor={doctor} />
</div> </div>
<Chart doctor={doctor} /> <Chart doctor={doctor} />
<Comment data={doctor} /> <Comment data={doctor} comments={comments} />
</div> </div>
); );
} }
@@ -7,12 +7,82 @@ import CloseModalD from "@/components/icons/CloseModalD";
import Field from "@/app/component/fields/Field"; import Field from "@/app/component/fields/Field";
import ArrowLeftD from "@/components/icons/ArrowLeftD"; import ArrowLeftD from "@/components/icons/ArrowLeftD";
import ProgressChange from "@/app/component/ProgressChange"; import ProgressChange from "@/app/component/ProgressChange";
import ProgressDetail from "@/data/progress_detail.json";
import Link from "next/link"; import Link from "next/link";
import { request } from "@/services/response";
function ModalAnswer({ doctor }) { function ModalAnswer({ doctor }) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false);
const handleOpen = () => setOpen(true); const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false); const handleClose = () => setOpen(false);
const [comment, setComment] = useState({
rate: ProgressDetail,
detail: {
comment: "",
doctor_id: doctor?.id,
},
});
const changeData = (parent, child, value) => {
if (parent === "rate") {
setComment((prevComment) => ({
...prevComment,
rate: prevComment.rate.map((item) =>
item.name === child ? { ...item, progress: value } : item
),
}));
} else {
setComment((prevComment) => ({
...prevComment,
[parent]: {
...prevComment[parent],
[child]: value,
},
}));
}
};
const averageRate =
comment.rate.reduce((sum, item) => sum + item.progress, 0) /
comment.rate.length /
20;
const sendReq = () => {
setLoading(true);
request
.postDoctorComment(comment.detail)
.then((res) => {
console.log(res);
let newComment = comment.rate;
newComment = {
correct_diagnosis: newComment[1].progress,
doctor_skill: newComment[4].progress,
behavior_doctor: newComment[2].progress,
office_cleaning: newComment[3].progress,
time_in_office: newComment[0].progress,
doctor: doctor?.id,
rate: averageRate,
};
console.log(newComment);
request
.postDoctorRate(newComment)
.then((res) => {
setLoading(false);
setOpen(false);
console.log(res);
})
.catch((err) => {
setLoading(false);
setOpen(false);
console.log(err);
});
})
.catch((err) => {
setLoading(false);
setOpen(false);
console.log(err);
});
};
return ( return (
<> <>
@@ -39,6 +109,7 @@ function ModalAnswer({ doctor }) {
</div> </div>
<Rating <Rating
className="!my-[24px]" className="!my-[24px]"
value={averageRate}
dir="ltr" dir="ltr"
sx={{ sx={{
"& .MuiSvgIcon-root": { "& .MuiSvgIcon-root": {
@@ -48,8 +119,13 @@ function ModalAnswer({ doctor }) {
}} }}
/> />
<ul className="flex w-full lg:w-fit flex-col items-start justify-start gap-1.5"> <ul className="flex w-full lg:w-fit flex-col items-start justify-start gap-1.5">
{doctor?.progress_detail?.map((item, idx) => ( {comment.rate?.map((item, idx) => (
<ProgressChange data={item} key={idx} /> <ProgressChange
key={idx}
data={item}
parent="rate"
changeData={changeData}
/>
))} ))}
</ul> </ul>
<div className="flex flex-col gap-[16px] justify-center items-start mt-[24px]"> <div className="flex flex-col gap-[16px] justify-center items-start mt-[24px]">
@@ -57,19 +133,20 @@ function ModalAnswer({ doctor }) {
<Field <Field
type="number" type="number"
multiline={true} multiline={true}
handleChange={(_, val) => changeData("detail", "comment", val)}
title="نظر خود را بنویسید..." title="نظر خود را بنویسید..."
/> />
<Link href={`/appointment/${doctor?.id}`} className="!mr-auto"> <Button
<Button onClick={sendReq}
variant="contained" loading={loading}
className="!py-2.5 !px-4 gap-1 !text-[14px] !font-medium !mt-[32px] !shadownon" variant="contained"
> className="!py-2.5 !mr-auto !px-4 gap-1 !text-[14px] !font-medium !mt-[32px] !shadownon"
دریافت نوبت >
<div className="w-[20px] h-[20px]"> ثبت نظر
<ArrowLeftD /> <div className="w-[20px] h-[20px]">
</div> <ArrowLeftD />
</Button> </div>
</Link> </Button>
</div> </div>
</Box> </Box>
</Modal> </Modal>
+2 -2
View File
@@ -5,7 +5,7 @@ import AboutDcotor from "./cards/AboutDcotor";
import Comments from "./cards/comments"; import Comments from "./cards/comments";
import Locations from "./cards/locations"; import Locations from "./cards/locations";
function DetailDoctor({ doctor }) { function DetailDoctor({ doctor, comments }) {
return ( return (
<div className="w-full lg:w-[59%]"> <div className="w-full lg:w-[59%]">
<div className="hidden lg:flex"> <div className="hidden lg:flex">
@@ -15,7 +15,7 @@ function DetailDoctor({ doctor }) {
<Link /> <Link />
<AboutDcotor doctor={doctor} /> <AboutDcotor doctor={doctor} />
<Locations doctor={doctor} /> <Locations doctor={doctor} />
<Comments doctor={doctor} /> <Comments doctor={doctor} comments={comments} />
</div> </div>
); );
} }
+4 -3
View File
@@ -2,9 +2,10 @@ import AppointmentList from "./appointmentList";
import DetailDoctor from "./detailDoctor"; import DetailDoctor from "./detailDoctor";
import Share from "./detailDoctor/Share"; import Share from "./detailDoctor/Share";
import CustomLoading from "@/app/component/loading/Custom"; import CustomLoading from "@/app/component/loading/Custom";
import Poster from "./poster";
function DoctorPage({ doctor, doctors }) { function DoctorPage({ doctor, doctors, comments }) {
console.log(comments);
return ( return (
<div className="pt-[92px] sm:pt-[120px] mt:pt-[148px] lg:pt-[176px] mt-[px] padding-responsive"> <div className="pt-[92px] sm:pt-[120px] mt:pt-[148px] lg:pt-[176px] mt-[px] padding-responsive">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@@ -24,7 +25,7 @@ function DoctorPage({ doctor, doctors }) {
</div> </div>
</div> </div>
<div className="flex items-start justify-center gap-6 mt-[30px]"> <div className="flex items-start justify-center gap-6 mt-[30px]">
<DetailDoctor doctor={doctor} /> <DetailDoctor doctor={doctor} comments={comments} />
<AppointmentList doctors={doctors} /> <AppointmentList doctors={doctors} />
</div> </div>
</div> </div>
-2
View File
@@ -23,8 +23,6 @@ function DoctorsPage({ params, matchedCity, matchedState, list }) {
const findItem = (id) => specialties.find((item) => item.id === id); const findItem = (id) => specialties.find((item) => item.id === id);
const isParent = findItem(fieldId) && findItem(fieldId).parent; const isParent = findItem(fieldId) && findItem(fieldId).parent;
const findSpecial = (id) => specialties.find((item) => item.id === id);
const [data, setData] = useState({ const [data, setData] = useState({
category: isParent ? findItem(isParent) : findItem(fieldId), category: isParent ? findItem(isParent) : findItem(fieldId),
specialties: isParent && findItem(fieldId), specialties: isParent && findItem(fieldId),
+1 -1
View File
@@ -14,7 +14,7 @@ function ListDoctors({ loading, list }) {
</ul> </ul>
) : ( ) : (
<p className="text-center py-10 text-gray-500"> <p className="text-center py-10 text-gray-500">
هیچ دکتری وجود ندارد! دکتری برای نمایش وجود ندارد.
</p> </p>
)} )}
</div> </div>
+27
View File
@@ -0,0 +1,27 @@
[
{
"name": "time_in_office",
"label": "زمان انتظار در مطب",
"progress": 70
},
{
"name": "correct_diagnosis",
"label": "تشخیص درست",
"progress": 70
},
{
"name": "behavior_doctor",
"label": "برخورد مناسب پزشک",
"progress": 70
},
{
"name": "office_cleaning",
"label": "نظافت مطب",
"progress": 70
},
{
"name": "doctor_skill",
"label": "مهارت پزشک",
"progress": 70
}
]
+2
View File
@@ -76,4 +76,6 @@ export const request = {
}), }),
getDoctorServices: () => getDoctorServices: () =>
api.get("api/v1/categorys/doctor_services", removeTokenHead), api.get("api/v1/categorys/doctor_services", removeTokenHead),
postDoctorComment: (data) => api.post("api/v1/clinicpro/comment", data),
postDoctorRate: (data) => api.post("api/v1/clinicpro/rate", data)
}; };