From dfc8492afab4ca353508b23a0fe1c5729850cea6 Mon Sep 17 00:00:00 2001 From: ehsan Date: Sat, 20 Sep 2025 17:04:36 +0330 Subject: [PATCH] handle like comments data, req for update comments and add loading for like comment && search specialty with searchbar in doctors page && set category and specialty data --- app/component/comment/AnswerField.js | 27 +++ app/component/comment/Field.js | 3 +- app/component/comment/ItemUser.js | 181 +++++++++--------- app/component/comment/Replies.js | 23 +++ app/component/comment/SendAnswer.js | 59 ++++++ app/component/comment/index.js | 2 - .../cards/comments/chart/index.js | 3 - .../cards/comments/modal/ModalAnswer.js | 3 +- components/doctors/search/AutoComplete.js | 79 ++++++++ components/doctors/search/SearchField.js | 38 +++- components/doctors/search/SendReq.js | 42 ++-- .../searchHead/smSearch/AutoCompleteSearch.js | 1 - helper/index.js | 25 +++ services/response.js | 1 + 14 files changed, 366 insertions(+), 121 deletions(-) create mode 100644 app/component/comment/AnswerField.js create mode 100644 app/component/comment/Replies.js create mode 100644 app/component/comment/SendAnswer.js create mode 100644 components/doctors/search/AutoComplete.js diff --git a/app/component/comment/AnswerField.js b/app/component/comment/AnswerField.js new file mode 100644 index 0000000..3e9fd32 --- /dev/null +++ b/app/component/comment/AnswerField.js @@ -0,0 +1,27 @@ +import ArrowUpComment from "@/components/icons/ArrowUpComment"; +import { Button } from "@mui/material"; + +function AnswerField({ data, update, setUpdate }) { + return ( +
+ +
+ ); +} + +export default AnswerField; diff --git a/app/component/comment/Field.js b/app/component/comment/Field.js index 01f9796..f33a69a 100644 --- a/app/component/comment/Field.js +++ b/app/component/comment/Field.js @@ -1,12 +1,13 @@ import { TextField } from "@mui/material"; -function Field({ title, type, multiline, isPlaceHolder }) { +function Field({ title, type, updateValue, value, multiline, isPlaceHolder }) { return ( updateValue(e.target.value)} sx={{ "& .MuiFormLabel-root": { top: "-4px !important", diff --git a/app/component/comment/ItemUser.js b/app/component/comment/ItemUser.js index 31a6183..31001d5 100644 --- a/app/component/comment/ItemUser.js +++ b/app/component/comment/ItemUser.js @@ -1,28 +1,73 @@ import Image from "next/image"; -import { Button, Skeleton } from "@mui/material"; -import Field from "./Field"; +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 ArrowUpComment from "@/components/icons/ArrowUpComment"; import LikeComment from "@/components/icons/LikeComment"; +import Replies from "./Replies"; +import AnswerField from "./AnswerField"; +import SendAnswer from "./SendAnswer"; +import { timeAgo } from "@/helper"; +import { request } from "@/services/response"; +import { useState } from "react"; -function ItemUser({ update, setUpdate, isReply, list, setList, data }) { - const handleLike = (lengthLike, data, nameLike, nameIsLike) => { - if (nameIsLike === "is_like" && data.is_dislike) { - data.is_dislike = false; - data.like_status.dislike_count--; - } else if ( - nameIsLike === "is_dislike" && - data.like_status.current_user_like.like - ) { - data.like_status.current_user_like.like = false; - data.like_status.like_count--; +function ItemUser({ update, setUpdate, data }) { + const [loadingLike, setLoadingLike] = useState(false); + const [loadingDislike, setLoadingDislike] = useState(false); + + const SendReq = async (type) => { + if (type === "like") setLoadingLike(true); + else setLoadingDislike(true); + + const body = { + comment_id: data.id, + like: type === "like" ? 1 : 0, + }; + + try { + const res = await request.postCommentsLike(body); + console.log(res); + } catch (err) { + console.log(err); + } finally { + if (type === "like") setLoadingLike(false); + else setLoadingDislike(false); } - data[nameLike] = lengthLike; - data[nameIsLike] = !data[nameIsLike]; + }; + + const handleLike = (data, type) => { + SendReq(type); + + const { current_user_like } = data.like_status; + + if (type === "like") { + if (current_user_like.like) { + data.like_status.like_count -= 1; + current_user_like.like = false; + } else { + data.like_status.like_count += 1; + current_user_like.like = true; + if (current_user_like.dislike) { + data.like_status.dislike_count -= 1; + current_user_like.dislike = false; + } + } + } else { + if (current_user_like.dislike) { + data.like_status.dislike_count -= 1; + current_user_like.dislike = false; + } else { + data.like_status.dislike_count += 1; + current_user_like.dislike = true; + if (current_user_like.like) { + data.like_status.like_count -= 1; + current_user_like.like = false; + } + } + } + setUpdate(!update); }; @@ -30,7 +75,7 @@ function ItemUser({ update, setUpdate, isReply, list, setList, data }) {
  • - 3 روز پیش + {timeAgo(data.created)}

    -

    +

    {data.comment}

    +
    + {/* لایک */}
    - handleLike( - data.like_status.current_user_like.like - ? data.like_status.like_count - 1 - : data.like_status.like_count + 1, - data, - "like", - "is_like" - ) - } + onClick={() => handleLike(data, "like")} > - {data.like_status.current_user_like.like ? ( + {loadingLike ? ( + + ) : data.like_status.current_user_like.like ? ( ) : ( )}
    -

    +

    {data.like_status.like_count || ""}

    + + {/* دیس‌لایک */}
    - handleLike( - data.is_dislike - ? data.like_status.dislike_count - 1 - : data.like_status.dislike_count + 1, - data, - "dislike", - "is_dislike" - ) - } + onClick={() => handleLike(data, "dislike")} > - {data.is_dislike ? : } + {loadingDislike ? ( + + ) : data.like_status.current_user_like.dislike ? ( + + ) : ( + + )}
    -

    +

    {data.like_status.dislike_count || ""}

    +
    -
    - - -
    + + {!!data.replies.length && ( -
    - -
    + )} -
      - {data.replies.map((item, idx) => ( - - ))} -
    +
  • ); diff --git a/app/component/comment/Replies.js b/app/component/comment/Replies.js new file mode 100644 index 0000000..c8e589f --- /dev/null +++ b/app/component/comment/Replies.js @@ -0,0 +1,23 @@ +import React from "react"; +import ItemUser from "./ItemUser"; + +function Replies({ data, update, setUpdate }) { + return ( + + ); +} + +export default Replies; diff --git a/app/component/comment/SendAnswer.js b/app/component/comment/SendAnswer.js new file mode 100644 index 0000000..0bd2798 --- /dev/null +++ b/app/component/comment/SendAnswer.js @@ -0,0 +1,59 @@ +import { Button } from "@mui/material"; +import Field from "./Field"; +import { useState } from "react"; +import { request } from "@/services/response"; + +function SendAnswer({ data }) { + const [comment, setComment] = useState(""); + const [loading, setLoading] = useState(false); + + const resetData = () => { + setComment(""); + setLoading(false); + data.is_open_answer = false; + }; + + const sendReq = () => { + setLoading(true); + const body = { + comment, + doctor_id: data?.doctor?.id, + parent: data?.parent, + }; + + request + .postDoctorComment(body) + .then(() => { + resetData(); + }) + .catch(() => { + resetData(); + }); + }; + + return ( +
    + setComment(val)} + /> + +
    + ); +} + +export default SendAnswer; diff --git a/app/component/comment/index.js b/app/component/comment/index.js index 03af5ed..eed81f7 100644 --- a/app/component/comment/index.js +++ b/app/component/comment/index.js @@ -17,10 +17,8 @@ function Comment({ data, comments }) { comments.map((item, idx) => ( ))} diff --git a/components/doctor/detailDoctor/cards/comments/chart/index.js b/components/doctor/detailDoctor/cards/comments/chart/index.js index e9b0b74..19f3059 100644 --- a/components/doctor/detailDoctor/cards/comments/chart/index.js +++ b/components/doctor/detailDoctor/cards/comments/chart/index.js @@ -7,9 +7,6 @@ import TextLoading from "@/app/component/loading/Text"; import CustomLoading from "@/app/component/loading/Custom"; function Chart({ comments, doctor }) { - console.log(doctor); - console.log(comments); - return (
    { + clearText(); + }, + }, + } + : {}; + console.log(inputValue); + + return ( + option.name} + disableClearable + inputValue={inputValue || ""} + className="!w-full" + onInputChange={(_, newInputValue) => { + setInputValue && setInputValue(newInputValue); + handleSearch(newInputValue); + }} + onChange={(_, newValue) => { + setSelectedOption && setSelectedOption(newValue); + handleSearch(newValue ? newValue.name : ""); + }} + // componentsProps={clearIndicatorProps} + renderOption={(props, option) => ( +
  • + {option.name} +
  • + )} + sx={{ + "& .MuiAutocomplete-input": { + background: noneBg ? "" : "#ffffff !important", + }, + }} + renderInput={(params) => ( + + )} + /> + ); +} + +export default AutoComplete; diff --git a/components/doctors/search/SearchField.js b/components/doctors/search/SearchField.js index bf8753f..6d2ff88 100644 --- a/components/doctors/search/SearchField.js +++ b/components/doctors/search/SearchField.js @@ -1,7 +1,7 @@ -import AutoCompleteSearch from "@/components/searchHead/smSearch/AutoCompleteSearch"; -import specialties from "@/data/specialties.json"; import { clearFilterParams } from "@/helper"; import { useRouter } from "next/navigation"; +import specialties from "@/data/specialties.json"; +import AutoComplete from "./AutoComplete"; function SearchField({ filter, setFilter, updateData, setDataInURL }) { const router = useRouter(); @@ -19,13 +19,43 @@ function SearchField({ filter, setFilter, updateData, setDataInURL }) { setFilter(newFilter); // setDataInURL(newFilter); }; + + console.log(filter); + + const handleSearch = (e) => { + const findName = specialties.find((item) => item.name === e); + console.log(findName); + + if (findName) { + let newFilter = { ...filter }; + if (findName.parent) { + const itemParent = specialties.find( + (item) => item.id === findName.parent + ); + newFilter.category = itemParent; + newFilter.specialty = findName; + // updateData("name", e); + } else { + newFilter.category = findName; + newFilter.specialty = ""; + } + newFilter.name = e + setFilter(newFilter); + // const newFilter = { ...filter, [name]: e }; + // setFilter(newFilter); + } else { + updateData("name", e); + } + }; + return (
    - updateData("name", e)} + handleSearch={handleSearch} + // handleSearch={(e) => updateData("name", e)} placeholder="جستجوی نام پزشک، تخصص، بیماری ..." />
    diff --git a/components/doctors/search/SendReq.js b/components/doctors/search/SendReq.js index 2710028..c4ebe84 100644 --- a/components/doctors/search/SendReq.js +++ b/components/doctors/search/SendReq.js @@ -5,33 +5,33 @@ import specialties from "@/data/specialties.json"; function SendReq({ filter, setFilter, setDataInURL, sendReq }) { const [loading, setLoading] = useState(false); - const checkName = () => { - let newFilter = filter; - const specialtyItem = - filter.name && - specialties.find((item) => item.name.includes(filter.name)); + // const checkName = () => { + // let newFilter = filter; + // const specialtyItem = + // filter.name && + // specialties.find((item) => item.name.includes(filter.name)); - if (specialtyItem) { - if (specialtyItem.parent) { - newFilter.specialty = specialtyItem; - newFilter.category = specialties.find( - (item) => item.id === specialtyItem.parent - ); - } else { - newFilter.specialty = specialtyItem; - } - } + // if (specialtyItem) { + // if (specialtyItem.parent) { + // newFilter.specialty = specialtyItem; + // newFilter.category = specialties.find( + // (item) => item.id === specialtyItem.parent + // ); + // } else { + // newFilter.specialty = specialtyItem; + // } + // } - return newFilter; - }; + // return newFilter; + // }; const filterData = () => { setLoading(true); - const newFilter = checkName(); - setFilter(newFilter); - setDataInURL(newFilter); + // const newFilter = checkName(); + setFilter(filter); + setDataInURL(filter); - sendReq(newFilter).finally(() => { + sendReq(filter).finally(() => { setLoading(false); }); }; diff --git a/components/searchHead/smSearch/AutoCompleteSearch.js b/components/searchHead/smSearch/AutoCompleteSearch.js index f575894..77bae1b 100644 --- a/components/searchHead/smSearch/AutoCompleteSearch.js +++ b/components/searchHead/smSearch/AutoCompleteSearch.js @@ -10,7 +10,6 @@ function AutoCompleteSearch({ setInputValue, setSelectedOption, }) { - // ایجاد props شرطی برای clearIndicator const clearIndicatorProps = clearText ? { clearIndicator: { diff --git a/helper/index.js b/helper/index.js index f225fbc..dd101fd 100644 --- a/helper/index.js +++ b/helper/index.js @@ -435,3 +435,28 @@ export const buildDoctorParams = (searchParams) => { return params; }; + +export function timeAgo(timestamp) { + const now = moment(); + const then = moment.unix(timestamp); + const diffSeconds = now.diff(then, "seconds"); + + if (diffSeconds < 60) { + return diffSeconds === 0 ? "همین الان" : `${diffSeconds} ثانیه پیش`; + } else if (diffSeconds < 3600) { + const minutes = Math.floor(diffSeconds / 60); + return `${minutes} دقیقه پیش`; + } else if (diffSeconds < 86400) { + const hours = Math.floor(diffSeconds / 3600); + return `${hours} ساعت پیش`; + } else if (diffSeconds < 2592000) { + const days = Math.floor(diffSeconds / 86400); + return `${days} روز پیش`; + } else if (diffSeconds < 31104000) { + const months = Math.floor(diffSeconds / 2592000); + return `${months} ماه پیش`; + } else { + const years = Math.floor(diffSeconds / 31104000); + return `${years} سال پیش`; + } +} diff --git a/services/response.js b/services/response.js index 8c15fc0..d8e3331 100644 --- a/services/response.js +++ b/services/response.js @@ -85,4 +85,5 @@ export const request = { 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), + postCommentsLike: (data) => api.post("/api/v1/clinicpro/like", data), };