From 4e0a44e5a4a88f35f99b78af75bea6096058fe49 Mon Sep 17 00:00:00 2001 From: M1kola Date: Thu, 1 Jun 2023 03:21:41 +0300 Subject: [PATCH] add comments to tasks --- .../TrackerTaskComment/TrackerTaskComment.jsx | 125 ++++++++++++++++ src/components/UI/ModalTicket/ModalTicket.jsx | 139 +++++++----------- .../UI/ModalTicket/ModalTicket.scss | 52 +++++++ .../UI/TicketFullScreen/TicketFullScreen.jsx | 134 ++++++++--------- 4 files changed, 290 insertions(+), 160 deletions(-) create mode 100644 src/components/TrackerTaskComment/TrackerTaskComment.jsx diff --git a/src/components/TrackerTaskComment/TrackerTaskComment.jsx b/src/components/TrackerTaskComment/TrackerTaskComment.jsx new file mode 100644 index 00000000..7dc50b23 --- /dev/null +++ b/src/components/TrackerTaskComment/TrackerTaskComment.jsx @@ -0,0 +1,125 @@ +import React, { useState } from "react"; +import TrackerTaskSubComment from "../TrackerTaskComment/TrackerTaskComment"; +import { apiRequest } from "../../api/request"; +import {urlForLocal} from "../../helper"; +import {getCorrectDate} from "../Calendar/calendarHelper"; +import edit from "../../images/edit.svg"; +import del from "../../images/delete.svg"; +import accept from "../../images/accept.png"; + +export const TrackerTaskComment = ({ + taskId, + comment, + commentDelete, + addSubComment, + subCommentDelete +}) => { + const [commentsEditOpen, setCommentsEditOpen] = useState(false) + const [commentsEditText, setCommentsEditText] = useState(comment.text) + const [subCommentsCreateOpen, setSubCommentsCreateOpen] = useState(false) + const [subCommentsCreateText, setSubCommentsCreateText] = useState('') + + function editComment() { + if (commentsEditText === comment.text) return + apiRequest("/comment/update", { + method: "PUT", + data: { + comment_id: comment.id, + text: commentsEditText + } + }).then(() => { + }) + } + + function deleteComment() { + apiRequest("/comment/update", { + method: "PUT", + data: { + comment_id: comment.id, + status: 0 + } + }).then(() => { + if (comment.parent_id) { + subCommentDelete(comment) + } else { + commentDelete(comment) + } + }) + } + + function createSubComment() { + setSubCommentsCreateOpen(false) + if(!subCommentsCreateText) return + apiRequest("/comment/create", { + method: "POST", + data: { + text: subCommentsCreateText, + entity_type: 2, + entity_id: taskId, + parent_id: comment.id + } + }).then((res) => { + let newSubComment = res + newSubComment.created_at = new Date() + setSubCommentsCreateText('') + addSubComment(comment.id, newSubComment) + }) + } + + return ( +
+
+
+ avatar +

{comment.user.fio}

+
+
+ {getCorrectDate(comment.created_at)} +
+ edit { + if (commentsEditOpen) { + editComment() + } + setCommentsEditOpen(!commentsEditOpen) + }} /> +
+ delete deleteComment()} /> +
+
+ {commentsEditOpen ? + { + setCommentsEditText(e.target.value) + }} /> : +

{commentsEditText}

} + {!comment.parent_id && + <> + { + subCommentsCreateOpen ? +
+ { + setSubCommentsCreateText(e.target.value) + }}/> + accept { + createSubComment() + }} + /> +
+ : + { + setSubCommentsCreateOpen(true) + }} className='comments__list__item__answer'>Ответить + } + + } + {Boolean(comment.subComments?.length) && comment.subComments.map((subComment) => { + return + }) + } +
+ ) +} + +export default TrackerTaskComment diff --git a/src/components/UI/ModalTicket/ModalTicket.jsx b/src/components/UI/ModalTicket/ModalTicket.jsx index bcc76ebf..82b162fc 100644 --- a/src/components/UI/ModalTicket/ModalTicket.jsx +++ b/src/components/UI/ModalTicket/ModalTicket.jsx @@ -1,6 +1,7 @@ import React, {useEffect, useState} from "react"; import { Link } from "react-router-dom"; import TrackerModal from "../TrackerModal/TrackerModal"; +import TrackerTaskComment from "../../../components/TrackerTaskComment/TrackerTaskComment"; import { apiRequest } from "../../../api/request"; import { useDispatch } from "react-redux"; import { @@ -8,8 +9,6 @@ import { setProjectBoardFetch, } from "../../../redux/projectsTrackerSlice"; -import {getCorrectDate} from '../../../components/Calendar/calendarHelper' - import category from "../../../images/category.png"; import watch from "../../../images/watch.png"; import file from "../../../images/fileModal.svg"; @@ -25,7 +24,6 @@ import close from "../../../images/closeProjectPersons.svg"; import "./ModalTicket.scss"; import {urlForLocal, getCorrectRequestDate} from "../../../helper"; -import accept from "../../../images/accept.png"; export const ModalTiсket = ({ active, @@ -40,9 +38,6 @@ export const ModalTiсket = ({ const [editOpen, setEditOpen] = useState(false); const [inputsValue, setInputsValue] = useState({title: task.title, description: task.description, comment: ''}); const [comments, setComments] = useState([]); - const [commentsEditOpen, setCommentsEditOpen] = useState({}) - const [commentsEditText, setCommentsEditText] = useState({}) - const [subCommentsCreateOpen, setSubCommentsCreateOpen] = useState({}) const [dropListOpen, setDropListOpen] = useState(false) const [dropListMembersOpen, setDropListMembersOpen] = useState(false) const [executor, setExecutor] = useState(task.executor) @@ -94,49 +89,48 @@ export const ModalTiсket = ({ }).then((res) => { let newComment = res newComment.created_at = new Date() + newComment.subComments = [] setInputsValue((prevValue) => ({...prevValue, comment: ''})) setComments((prevValue) => ([...prevValue, newComment])) - setCommentsEditOpen((prevValue) => ({...prevValue, [res.id]: false})) - setCommentsEditText((prevValue) => ({...prevValue, [res.id]: res.text})) }) } - function deleteComment(commentId) { - apiRequest("/comment/update", { - method: "PUT", - data: { - comment_id: commentId, - status: 0 + + function commentDelete(comment) { + setComments((prevValue) => prevValue.filter((item) => item.id !== comment.id)) + if (comment.subComments.length) { + comment.subComments.forEach((subComment) => { + apiRequest("/comment/update", { + method: "PUT", + data: { + comment_id: subComment.id, + status: 0 + } + }).then(() => { + }) + }) + } + } + + function addSubComment(commentId, subComment) { + const addSubComment = comments + addSubComment.forEach((comment) => { + if (comment.id === commentId) { + comment.subComments.push(subComment) } - }).then((res) => { - setComments((prevValue) => prevValue.filter((item) => item.id !== commentId)) }) + setComments(addSubComment) } - function editComment(commentId) { - - apiRequest("/comment/update", { - method: "PUT", - data: { - comment_id: commentId, - text: commentsEditText[commentId] + function subCommentDelete(subComment) { + const deleteSubComment = comments + deleteSubComment.forEach((comment, index) => { + if (comment.id === subComment.parent_id) { + deleteSubComment[index].subComments = comment.subComments.filter((item) => item.id !== subComment.id) } - }).then((res) => { - // createSubComment() }) + setComments([...deleteSubComment]) } - // function createSubComment() { - // apiRequest("/comment/create", { - // method: "POST", - // data: { - // text: '12312312', - // entity_type: 2, - // entity_id: task.id, - // parent_id: 36 - // } - // }).then((res) => console.log(res)) - // } - function startTaskTimer() { apiRequest("/timer/create", { method: "POST", @@ -159,7 +153,7 @@ export const ModalTiсket = ({ timer_id: timerInfo.id, stopped_at: getCorrectRequestDate(new Date()) } - }).then((res) => { + }).then(() => { setTimerStart(false) clearInterval(timerId) }) @@ -185,7 +179,7 @@ export const ModalTiсket = ({ task_id: task.id, executor_id: 0 }, - }).then((res) => { + }).then(() => { setExecutor(null) }); } @@ -210,19 +204,24 @@ export const ModalTiсket = ({ task_id: task.id, user_id: person.user_id }, - }).then((res) => { + }).then(() => { setMembers(members.filter((item) => item.user_id !== person.user_id)) }); } useEffect(() => { apiRequest(`/comment/get-by-entity?entity_type=2&entity_id=${task.id}`).then((res) => { - setComments(res) - res.forEach((item) => { - setCommentsEditOpen((prevValue) => ({...prevValue, [item.id]: false})) - setCommentsEditText((prevValue) => ({...prevValue, [item.id]: item.text})) - setSubCommentsCreateOpen((prevValue) => ({...prevValue, [item.id]: false})) - }) + const comments = res.reduce((acc, cur) => { + if (!cur.parent_id) { + acc.push({...cur, subComments: []}) + } else { + acc.forEach((item) => { + if (item.id === cur.parent_id) item.subComments.push(cur) + }) + } + return acc + }, []) + setComments(comments) }) apiRequest(`/timer/get-by-entity?entity_type=2&entity_id=${task.id}`).then((res) => { let timerSeconds = 0 @@ -347,45 +346,15 @@ export const ModalTiсket = ({
{comments.map((comment) => { - return
-
-
- avatar -

{comment.user.fio}

-
-
- {getCorrectDate(comment.created_at)} -
- edit { - if (commentsEditOpen[comment.id]) { - editComment(comment.id) - } - setCommentsEditOpen((prevValue) => ({...prevValue, [comment.id]: !prevValue[comment.id]})) - }} /> -
- delete deleteComment(comment.id)} /> -
-
- {commentsEditOpen[comment.id] ? { - setCommentsEditText((prevValue) => ({...prevValue, [comment.id]: e.target.value})) - }} /> :

{commentsEditText[comment.id]}

} - {subCommentsCreateOpen[comment.id] ? -
- - accept { - setSubCommentsCreateOpen((prevValue) => ({...prevValue, [comment.id]: !prevValue[comment.id]})) - }} - /> -
- : - { - setSubCommentsCreateOpen((prevValue) => ({...prevValue, [comment.id]: !prevValue[comment.id]})) - }} className='comments__list__item__answer'>Ответить - } -
- }) - + return + }) }
diff --git a/src/components/UI/ModalTicket/ModalTicket.scss b/src/components/UI/ModalTicket/ModalTicket.scss index a4436d78..cb71b885 100644 --- a/src/components/UI/ModalTicket/ModalTicket.scss +++ b/src/components/UI/ModalTicket/ModalTicket.scss @@ -81,6 +81,22 @@ flex-direction: column; max-height: 420px; overflow: auto; + + &::-webkit-scrollbar { + width: 4px; + border-radius: 10px; + } + + &::-webkit-scrollbar-thumb { + background: #cbd9f9; + border-radius: 10px; + } + + &::-webkit-scrollbar-track { + background: #c5c0c6; + border-radius: 10px; + } + &__item { padding: 10px 20px; display: flex; @@ -89,6 +105,41 @@ border-radius: 44px; font-size: 14px; width: 100%; + position: relative; + + &__subComment { + &:before { + content: ''; + background: #E4E4E6; + height: 1px; + width: 7px; + position: absolute; + top: 36%; + left: 2.5%; + } + } + + &__main { + &:after { + content: ''; + position: absolute; + background-image: url("../../../images/mainTaskCommentImg.png"); + width: 10px; + height: 8px; + top: 50px; + left: 25px; + } + + &:before { + content: ''; + position: absolute; + background: #E4E4E6; + width: 1px; + height: calc(100% - 120px); + left: 29px; + top: 65px; + } + } &__fio { display: flex; @@ -163,6 +214,7 @@ padding: 3px 5px; display: flex; align-items: center; + margin-top: 5px; img { width: 20px; diff --git a/src/components/UI/TicketFullScreen/TicketFullScreen.jsx b/src/components/UI/TicketFullScreen/TicketFullScreen.jsx index d2adb881..0f0ad150 100644 --- a/src/components/UI/TicketFullScreen/TicketFullScreen.jsx +++ b/src/components/UI/TicketFullScreen/TicketFullScreen.jsx @@ -5,6 +5,7 @@ import { ProfileBreadcrumbs } from "../../ProfileBreadcrumbs/ProfileBreadcrumbs" import { Footer } from "../../Footer/Footer"; import { Link, useParams, useNavigate } from "react-router-dom"; import TrackerModal from "../TrackerModal/TrackerModal"; +import TrackerTaskComment from "../../../components/TrackerTaskComment/TrackerTaskComment"; import { Navigation } from "../../Navigation/Navigation"; import {Loader} from "../../Loader/Loader"; @@ -33,12 +34,10 @@ import link from "../../../images/link.svg"; import archive2 from "../../../images/archive.svg"; import del from "../../../images/delete.svg"; import edit from "../../../images/edit.svg"; -import accept from "../../../images/accept.png" import "./ticketFullScreen.scss"; import close from "../../../images/closeProjectPersons.svg"; import {getCorrectRequestDate, urlForLocal} from "../../../helper"; -import {getCorrectDate} from "../../Calendar/calendarHelper"; export const TicketFullScreen = ({}) => { const [modalAddWorker, setModalAddWorker] = useState(false); @@ -52,9 +51,6 @@ export const TicketFullScreen = ({}) => { const [inputsValue, setInputsValue] = useState({}); const [loader, setLoader] = useState(true); const [comments, setComments] = useState([]); - const [commentsEditOpen, setCommentsEditOpen] = useState({}) - const [subCommentsCreateOpen, setSubCommentsCreateOpen] = useState({}) - const [commentsEditText, setCommentsEditText] = useState({}) const [personListOpen, setPersonListOpen] = useState(false) const [timerStart, setTimerStart] = useState(false) const [timerInfo, setTimerInfo] = useState({}) @@ -64,12 +60,17 @@ export const TicketFullScreen = ({}) => { setTaskInfo(taskInfo); setInputsValue({title: taskInfo.title, description: taskInfo.description, comment: ''}) apiRequest(`/comment/get-by-entity?entity_type=2&entity_id=${taskInfo.id}`).then((res) => { - setComments(res) - res.forEach((item) => { - setCommentsEditOpen((prevValue) => ({...prevValue, [item.id]: false})) - setSubCommentsCreateOpen((prevValue) => ({...prevValue, [item.id]: false})) - setCommentsEditText((prevValue) => ({...prevValue, [item.id]: item.text})) - }) + const comments = res.reduce((acc, cur) => { + if (!cur.parent_id) { + acc.push({...cur, subComments: []}) + } else { + acc.forEach((item) => { + if (item.id === cur.parent_id) item.subComments.push(cur) + }) + } + return acc + }, []) + setComments(comments) }) taskInfo.timers.forEach((time) => { if (!time.stopped_at) { @@ -102,7 +103,7 @@ export const TicketFullScreen = ({}) => { title: inputsValue.title, description: inputsValue.description }, - }).then((res) => { + }).then(() => { }); } @@ -117,33 +118,9 @@ export const TicketFullScreen = ({}) => { }).then((res) => { let newComment = res newComment.created_at = new Date() + newComment.subComments = [] setInputsValue((prevValue) => ({...prevValue, comment: ''})) setComments((prevValue) => ([...prevValue, newComment])) - setCommentsEditOpen((prevValue) => ({...prevValue, [res.id]: false})) - setCommentsEditText((prevValue) => ({...prevValue, [res.id]: res.text})) - }) - } - - function deleteComment(commentId) { - apiRequest("/comment/update", { - method: "PUT", - data: { - comment_id: commentId, - status: 0 - } - }).then((res) => { - setComments((prevValue) => prevValue.filter((item) => item.id !== commentId)) - }) - } - - function editComment(commentId) { - apiRequest("/comment/update", { - method: "PUT", - data: { - comment_id: commentId, - text: commentsEditText[commentId] - } - }).then((res) => { }) } @@ -168,7 +145,7 @@ export const TicketFullScreen = ({}) => { timer_id: timerInfo.id, stopped_at: getCorrectRequestDate(new Date()) } - }).then((res) => setTimerStart(false)) + }).then(() => setTimerStart(false)) } function deletePerson(userId) { @@ -183,6 +160,42 @@ export const TicketFullScreen = ({}) => { }); } + function commentDelete(comment) { + setComments((prevValue) => prevValue.filter((item) => item.id !== comment.id)) + if (comment.subComments.length) { + comment.subComments.forEach((subComment) => { + apiRequest("/comment/update", { + method: "PUT", + data: { + comment_id: subComment.id, + status: 0 + } + }).then(() => { + }) + }) + } + } + + function addSubComment(commentId, subComment) { + const addSubComment = comments + addSubComment.forEach((comment) => { + if (comment.id === commentId) { + comment.subComments.push(subComment) + } + }) + setComments(addSubComment) + } + + function subCommentDelete(subComment) { + const deleteSubComment = comments + deleteSubComment.forEach((comment, index) => { + if (comment.id === subComment.parent_id) { + deleteSubComment[index].subComments = comment.subComments.filter((item) => item.id !== subComment.id) + } + }) + setComments([...deleteSubComment]) + } + const toggleTabs = (index) => { dispatch(setToggleTab(index)); }; @@ -341,43 +354,14 @@ export const TicketFullScreen = ({}) => {
{comments.map((comment) => { - return
-
-
- avatar -

{comment.user.fio}

-
-
- {getCorrectDate(comment.created_at)} -
- edit { - if (commentsEditOpen[comment.id]) { - editComment(comment.id) - } - setCommentsEditOpen((prevValue) => ({...prevValue, [comment.id]: !prevValue[comment.id]})) - }} /> -
- delete deleteComment(comment.id)} /> -
-
- {commentsEditOpen[comment.id] ? { - setCommentsEditText((prevValue) => ({...prevValue, [comment.id]: e.target.value})) - }} /> :

{commentsEditText[comment.id]}

} - {subCommentsCreateOpen[comment.id] ? -
- - accept { - setSubCommentsCreateOpen((prevValue) => ({...prevValue, [comment.id]: !prevValue[comment.id]})) - }} - /> -
- : - { - setSubCommentsCreateOpen((prevValue) => ({...prevValue, [comment.id]: !prevValue[comment.id]})) - }} className='comments__list__item__answer'>Ответить - } -
+ return }) }