Merge pull request #91 from apuc/tracker-connect-back
Tracker connect back
This commit is contained in:
		| @@ -8,6 +8,8 @@ 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"; | ||||
| @@ -33,7 +35,10 @@ export const ModalTiсket = ({ | ||||
|   const dispatch = useDispatch(); | ||||
|   const [addSubtask, setAddSubtask] = useState(false); | ||||
|   const [editOpen, setEditOpen] = useState(false); | ||||
|   const [inputsValue, setInputsValue] = useState({title: task.title, description: task.description}) | ||||
|   const [inputsValue, setInputsValue] = useState({title: task.title, description: task.description, comment: ''}); | ||||
|   const [comments, setComments] = useState([]); | ||||
|   const [commentsEditOpen, setCommentsEditOpen] = useState({}) | ||||
|   const [commentsEditText, setCommentsEditText] = useState({}) | ||||
|  | ||||
|   function deleteTask() { | ||||
|     apiRequest("/task/update-task", { | ||||
| @@ -61,6 +66,57 @@ export const ModalTiсket = ({ | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   function createComment() { | ||||
|     apiRequest("/comment/create", { | ||||
|       method: "POST", | ||||
|       data: { | ||||
|         text: inputsValue.comment, | ||||
|         entity_type: 2, | ||||
|         entity_id: task.id | ||||
|       } | ||||
|     }).then((res) => { | ||||
|       let newComment = res | ||||
|       newComment.created_at = new Date() | ||||
|       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) => { | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   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})) | ||||
|       }) | ||||
|     }) | ||||
|   }, []) | ||||
|  | ||||
|   return ( | ||||
|     <div | ||||
|       className={active ? "modal-tiket active" : "modal-tiket"} | ||||
| @@ -91,7 +147,7 @@ export const ModalTiсket = ({ | ||||
|               {editOpen ? <input value={inputsValue.description} onChange={(e) => { | ||||
|                 setInputsValue((prevValue) => ({...prevValue, description: e.target.value})) | ||||
|               }}/> :<p>{inputsValue.description}</p>} | ||||
|               <img src={taskImg} className="image-task"></img> | ||||
|               {/*<img src={taskImg} className="image-task"></img>*/} | ||||
|             </div> | ||||
|             <div className="content__communication"> | ||||
|               <p className="tasks"> | ||||
| @@ -115,8 +171,33 @@ export const ModalTiсket = ({ | ||||
|               </p> | ||||
|             </div> | ||||
|             <div className="content__input"> | ||||
|               <input placeholder="Оставить комментарий"></input> | ||||
|               <img src={send}></img> | ||||
|               <input placeholder="Оставить комментарий" value={inputsValue.comment} onChange={(e) => { | ||||
|                 setInputsValue((prevValue) => ({...prevValue, comment: e.target.value})) | ||||
|               }} /> | ||||
|               <img src={send} onClick={createComment}></img> | ||||
|             </div> | ||||
|             <div className='comments__list'> | ||||
|               {comments.map((comment) => { | ||||
|                 return <div className='comments__list__item' key={comment.id}> | ||||
|                   <div className='comments__list__item__info'> | ||||
|                     <span>{getCorrectDate(comment.created_at)}</span> | ||||
|                     <div className={commentsEditOpen[comment.id] ? 'edit edit__open' : 'edit'} > | ||||
|                       <img src={edit} alt='edit' onClick={() => { | ||||
|                         if (commentsEditOpen[comment.id]) { | ||||
|                           editComment(comment.id) | ||||
|                         } | ||||
|                         setCommentsEditOpen((prevValue) => ({...prevValue, [comment.id]: !prevValue[comment.id]})) | ||||
|                       }} /> | ||||
|                     </div> | ||||
|                     <img src={del} alt='delete' onClick={() => deleteComment(comment.id)} /> | ||||
|                   </div> | ||||
|                   {commentsEditOpen[comment.id] ? <input value={commentsEditText[comment.id]} onChange={(e) =>  { | ||||
|                     setCommentsEditText((prevValue) => ({...prevValue, [comment.id]: e.target.value})) | ||||
|                   }} /> : <p>{commentsEditText[comment.id]}</p>} | ||||
|                 </div> | ||||
|               }) | ||||
|  | ||||
|               } | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|   | ||||
| @@ -75,6 +75,46 @@ | ||||
|         color: #1a1919; | ||||
|         margin-bottom: 0; | ||||
|       } | ||||
|  | ||||
|       .comments__list { | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         row-gap: 10px; | ||||
|         &__item { | ||||
|           padding: 10px 20px; | ||||
|           display: flex; | ||||
|           flex-direction: column; | ||||
|           max-width: 438px; | ||||
|           background: #f1f1f1; | ||||
|           border-radius: 44px; | ||||
|           font-size: 14px; | ||||
|           width: 100%; | ||||
|           row-gap: 10px; | ||||
|  | ||||
|           &__info { | ||||
|             display: flex; | ||||
|             justify-content: center; | ||||
|             column-gap: 15px; | ||||
|  | ||||
|             .edit { | ||||
|               width: 25px; | ||||
|               display: flex; | ||||
|               align-items: center; | ||||
|               justify-content: center; | ||||
|               border-radius: 5px; | ||||
|             } | ||||
|  | ||||
|             .edit__open { | ||||
|               background: green; | ||||
|             } | ||||
|  | ||||
|             img { | ||||
|               cursor: pointer; | ||||
|               width: 15px; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     &__description { | ||||
|   | ||||
| @@ -8,8 +8,15 @@ import TrackerModal from "../TrackerModal/TrackerModal"; | ||||
| import { Navigation } from "../../Navigation/Navigation"; | ||||
| import {Loader} from "../../Loader/Loader"; | ||||
|  | ||||
| import { useDispatch } from "react-redux"; | ||||
| import {modalToggle, setToggleTab} from "../../../redux/projectsTrackerSlice"; | ||||
| import {useDispatch, useSelector} from "react-redux"; | ||||
| import { | ||||
|   deletePersonOnProject, | ||||
|   modalToggle, | ||||
|   setProjectBoardFetch, | ||||
|   setToggleTab, | ||||
|   getProjectBoard, | ||||
|   getBoarderLoader | ||||
| } from "../../../redux/projectsTrackerSlice"; | ||||
| import { apiRequest } from "../../../api/request"; | ||||
|  | ||||
| import project from "../../../images/trackerProject.svg"; | ||||
| @@ -22,7 +29,6 @@ import plus from "../../../images/plus.svg"; | ||||
| import tasks from "../../../images/trackerTasks.svg"; | ||||
| import archive from "../../../images/archiveTracker.svg"; | ||||
| import selectArrow from "../../../images/select.svg"; | ||||
| import avatarTest from "../../../images/AvatarTest .png"; | ||||
| import arrow from "../../../images/arrowCalendar.png"; | ||||
| import link from "../../../images/link.svg"; | ||||
| import archive2 from "../../../images/archive.svg"; | ||||
| @@ -30,28 +36,39 @@ import del from "../../../images/delete.svg"; | ||||
| import edit from "../../../images/edit.svg"; | ||||
|  | ||||
| import "./ticketFullScreen.scss"; | ||||
| import close from "../../../images/closeProjectPersons.svg"; | ||||
| import {urlForLocal} from "../../../helper"; | ||||
| import {getCorrectDate} from "../../Calendar/calendarHelper"; | ||||
|  | ||||
| export const TicketFullScreen = ({}) => { | ||||
|   const [modalAddWorker, setModalAddWorker] = useState(false); | ||||
|   const ticketId = useParams(); | ||||
|   const dispatch = useDispatch(); | ||||
|   const navigate = useNavigate(); | ||||
|   const [projectInfo, setProjectInfo] = useState({}); | ||||
|   const projectBoard = useSelector(getProjectBoard); | ||||
|   const boardLoader = useSelector(getBoarderLoader); | ||||
|   const [taskInfo, setTaskInfo] = useState({}); | ||||
|   const [editOpen, setEditOpen] = useState(false); | ||||
|   const [inputsValue, setInputsValue] = useState({}) | ||||
|   const [loader, setLoader] = useState(true) | ||||
|   const [inputsValue, setInputsValue] = useState({}); | ||||
|   const [loader, setLoader] = useState(true); | ||||
|   const [comments, setComments] = useState([]); | ||||
|   const [commentsEditOpen, setCommentsEditOpen] = useState({}) | ||||
|   const [commentsEditText, setCommentsEditText] = useState({}) | ||||
|   const [personListOpen, setPersonListOpen] = useState(false) | ||||
|  | ||||
|   useEffect(() => { | ||||
|     apiRequest(`/task/get-task?task_id=${ticketId.id}`).then((taskInfo) => { | ||||
|       setTaskInfo(taskInfo); | ||||
|       setInputsValue({title: taskInfo.title, description: taskInfo.description}) | ||||
|       apiRequest(`/project/get-project?project_id=${taskInfo.project_id}`).then( | ||||
|         (project) => { | ||||
|           setProjectInfo(project); | ||||
|           setLoader(false) | ||||
|         } | ||||
|       ); | ||||
|       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})) | ||||
|           setCommentsEditText((prevValue) => ({...prevValue, [item.id]: item.text})) | ||||
|         }) | ||||
|       }) | ||||
|       dispatch(setProjectBoardFetch(taskInfo.project_id)); | ||||
|       setLoader(boardLoader) | ||||
|     }); | ||||
|   }, []); | ||||
|  | ||||
| @@ -79,6 +96,59 @@ export const TicketFullScreen = ({}) => { | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   function createComment() { | ||||
|     apiRequest("/comment/create", { | ||||
|       method: "POST", | ||||
|       data: { | ||||
|         text: inputsValue.comment, | ||||
|         entity_type: 2, | ||||
|         entity_id: taskInfo.id | ||||
|       } | ||||
|     }).then((res) => { | ||||
|       let newComment = res | ||||
|       newComment.created_at = new Date() | ||||
|       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) => { | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   function deletePerson(userId) { | ||||
|     apiRequest("/project/del-user", { | ||||
|       method: "DELETE", | ||||
|       data: { | ||||
|         project_id: projectBoard.id, | ||||
|         user_id: userId | ||||
|       }, | ||||
|     }).then((res) => { | ||||
|       dispatch(deletePersonOnProject(userId)) | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   const toggleTabs = (index) => { | ||||
|     dispatch(setToggleTab(index)); | ||||
|   }; | ||||
| @@ -130,7 +200,7 @@ export const TicketFullScreen = ({}) => { | ||||
|         <div className="tracker__tabs__content content-tabs"> | ||||
|           <div className="tasks__head"> | ||||
|             <div className="tasks__head__wrapper"> | ||||
|               <h4>Проект : {projectInfo.name}</h4> | ||||
|               <h4>Проект : {projectBoard.name}</h4> | ||||
|  | ||||
|               <TrackerModal | ||||
|                 active={modalAddWorker} | ||||
| @@ -138,19 +208,45 @@ export const TicketFullScreen = ({}) => { | ||||
|               ></TrackerModal> | ||||
|  | ||||
|               <div className="tasks__head__persons"> | ||||
|                 <img src={avatarTest} alt="avatar" /> | ||||
|                 <img src={avatarTest} alt="avatar" /> | ||||
|                 <span className="countPersons">+9</span> | ||||
|                 {/*<img src={avatarTest} alt="avatar" />*/} | ||||
|                 {/*<img src={avatarTest} alt="avatar" />*/} | ||||
|                 <span className="countPersons">{projectBoard.projectUsers?.length}</span> | ||||
|                 <span | ||||
|                   className="addPerson" | ||||
|                   onClick={() => { | ||||
|                     dispatch(modalToggle("addWorker")); | ||||
|                     setModalAddWorker(true); | ||||
|                     setPersonListOpen(true) | ||||
|                   }} | ||||
|                 > | ||||
|                   + | ||||
|                 </span> | ||||
|                 <p>добавить участника в проект</p> | ||||
|                 <p>добавить участника</p> | ||||
|                 {personListOpen && | ||||
|                 <div className='persons__list'> | ||||
|                   <img className='persons__list__close' src={close} alt='close' onClick={() => setPersonListOpen(false)} /> | ||||
|                   <div className='persons__list__count'><span>{projectBoard.projectUsers?.length}</span>участник</div> | ||||
|                   <div className='persons__list__info'>В проекте - <span>“{projectBoard.name}”</span></div> | ||||
|                   <div className='persons__list__items'> | ||||
|                     {projectBoard.projectUsers?.map((person) => { | ||||
|                       return <div className='persons__list__item' key={person.user_id}> | ||||
|                         <img className='avatar' src={urlForLocal(person.user.avatar)} alt='avatar' /> | ||||
|                         <span>{person.user.fio}</span> | ||||
|                         <img className='delete' src={close} alt='delete' onClick={() => deletePerson(person.user_id)}/> | ||||
|                       </div> | ||||
|                     }) | ||||
|                     } | ||||
|                   </div> | ||||
|                   <div className='persons__list__add' | ||||
|                        onClick={() => { | ||||
|                          dispatch(modalToggle("addWorker")); | ||||
|                          setModalAddWorker(true); | ||||
|                          setPersonListOpen(false) | ||||
|                        }} | ||||
|                   > | ||||
|                     <span className='addPerson'>+</span> | ||||
|                     <p>Добавить участников</p> | ||||
|                   </div> | ||||
|                 </div> | ||||
|                 } | ||||
|               </div> | ||||
|               <div className="tasks__head__select"> | ||||
|                 <span>Учавствую</span> | ||||
| @@ -180,7 +276,7 @@ export const TicketFullScreen = ({}) => { | ||||
|                 {editOpen ? <input value={inputsValue.description} onChange={(e) => { | ||||
|                   setInputsValue((prevValue) => ({...prevValue, description: e.target.value})) | ||||
|                 }}/> :<p>{inputsValue.description}</p>} | ||||
|                 <img src={task} className="image-task"></img> | ||||
|                 {/*<img src={task} className="image-task"></img>*/} | ||||
|               </div> | ||||
|               <div className="content__communication"> | ||||
|                 <p className="tasks"> | ||||
| @@ -204,8 +300,33 @@ export const TicketFullScreen = ({}) => { | ||||
|                 </p> | ||||
|               </div> | ||||
|               <div className="content__input"> | ||||
|                 <input placeholder="Оставить комментарий"></input> | ||||
|                 <img src={send}></img> | ||||
|                 <input placeholder="Оставить комментарий" value={inputsValue.comment} onChange={(e) => { | ||||
|                   setInputsValue((prevValue) => ({...prevValue, comment: e.target.value})) | ||||
|                 }} /> | ||||
|                 <img src={send} onClick={createComment}></img> | ||||
|               </div> | ||||
|               <div className='comments__list'> | ||||
|                 {comments.map((comment) => { | ||||
|                   return <div className='comments__list__item' key={comment.id}> | ||||
|                     <div className='comments__list__item__info'> | ||||
|                       <span>{getCorrectDate(comment.created_at)}</span> | ||||
|                       <div className={commentsEditOpen[comment.id] ? 'edit edit__open' : 'edit'} > | ||||
|                         <img src={edit} alt='edit' onClick={() => { | ||||
|                           if (commentsEditOpen[comment.id]) { | ||||
|                             editComment(comment.id) | ||||
|                           } | ||||
|                           setCommentsEditOpen((prevValue) => ({...prevValue, [comment.id]: !prevValue[comment.id]})) | ||||
|                         }} /> | ||||
|                       </div> | ||||
|                       <img src={del} alt='delete' onClick={() => deleteComment(comment.id)} /> | ||||
|                     </div> | ||||
|                     {commentsEditOpen[comment.id] ? <input value={commentsEditText[comment.id]} onChange={(e) =>  { | ||||
|                       setCommentsEditText((prevValue) => ({...prevValue, [comment.id]: e.target.value})) | ||||
|                     }} /> : <p>{commentsEditText[comment.id]}</p>} | ||||
|                   </div> | ||||
|                 }) | ||||
|  | ||||
|                 } | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|   | ||||
| @@ -12,7 +12,8 @@ import { | ||||
|   editProjectName, | ||||
|   editColumnName, | ||||
|   getColumnName, | ||||
|   getColumnId | ||||
|   getColumnId, | ||||
|   addPersonToProject | ||||
| } from "../../../redux/projectsTrackerSlice"; | ||||
|  | ||||
| import arrowDown from "../../../images/selectArrow.png" | ||||
| @@ -136,23 +137,36 @@ export const TrackerModal = ({ | ||||
|     apiRequest("/project/add-user", { | ||||
|       method: "POST", | ||||
|       data: { | ||||
|         user_id: selectedWorker.id, | ||||
|         user_id: selectedWorker.user_id, | ||||
|         project_id: projectBoard.id | ||||
|       } | ||||
|     }).then((el) => { | ||||
|       dispatch(addPersonToProject(el)) | ||||
|       setActive(false); | ||||
|       selectedWorker(null) | ||||
|       setSelectedWorker('') | ||||
|       setSelectWorkersOpen(false) | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   useEffect(() => { | ||||
|     modalType === "addWorker" ? apiRequest('/project/my-employee').then((el) => setWorkers(el.managerEmployees)) : '' | ||||
|   }, [modalType]) | ||||
|     modalType === "addWorker" ? apiRequest('/project/my-employee').then((el) => { | ||||
|       let persons = el.managerEmployees | ||||
|       let ids = projectBoard.projectUsers.map((user) => user.user_id) | ||||
|       setWorkers(persons.reduce((acc, cur) => { | ||||
|         if (!ids.includes(cur.user_id)) acc.push(cur) | ||||
|         return acc | ||||
|       }, [])) | ||||
|     }) : '' | ||||
|   }, [active]) | ||||
|  | ||||
|  | ||||
|   return ( | ||||
|     <div | ||||
|       className={active ? "modal-add active" : "modal-add"} | ||||
|       onClick={() => setActive(false)} | ||||
|       onClick={() => { | ||||
|         setActive(false) | ||||
|         setSelectWorkersOpen(false) | ||||
|       }} | ||||
|     > | ||||
|       <div className="modal-add__content" onClick={(e) => e.stopPropagation()}> | ||||
|         {modalType === "addWorker" && ( | ||||
| @@ -170,18 +184,24 @@ export const TrackerModal = ({ | ||||
|                 <p>{selectedWorker ? selectedWorker.employee.fio : 'Выберите пользователя'}</p> | ||||
|                 <img className='arrow' src={arrowDown} alt='arrow' /> | ||||
|                 {Boolean(selectWorkersOpen) && | ||||
|                 <div className='select__worker__dropDown'> | ||||
|                   {workers.map((worker) => { | ||||
|                     if (worker === selectedWorker) { | ||||
|                       return | ||||
|                   <div className='select__worker__dropDown'> | ||||
|                     {Boolean(workers.length) ? | ||||
|                       workers.map((worker) => { | ||||
|                       if (worker === selectedWorker) { | ||||
|                         return | ||||
|                       } | ||||
|                       return <div className='worker' key={worker.id} onClick={() => | ||||
|                         { | ||||
|                           setSelectedWorker(worker) | ||||
|                         } | ||||
|                       }> | ||||
|                         <p>{worker.employee.fio}</p> | ||||
|                         <img src={urlForLocal(worker.employee.avatar)} alt='avatar'/> | ||||
|                       </div> | ||||
|                     }) : | ||||
|                         <div>Нет пользователей</div> | ||||
|                     } | ||||
|                     return <div className='worker' key={worker.id} onClick={() => setSelectedWorker(worker)}> | ||||
|                       <p>{worker.employee.fio}</p> | ||||
|                       <img src={urlForLocal(worker.employee.avatar)} alt='avatar'/> | ||||
|                     </div> | ||||
|                   }) | ||||
|                   } | ||||
|                 </div> | ||||
|                   </div> | ||||
|                 } | ||||
|               </div> | ||||
|             </div> | ||||
|   | ||||
							
								
								
									
										3
									
								
								src/images/closeProjectPersons.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/images/closeProjectPersons.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| <svg width="9" height="8" viewBox="0 0 9 8" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||
| <path d="M0.912203 -5.93063e-05C1.078 -0.00256001 1.17953 0.0699604 1.2738 0.164737C2.15005 1.04323 3.0278 1.92023 3.90504 2.79773C3.9859 2.87858 4.06642 2.87867 4.14661 2.79798C5.0061 1.93824 5.8656 1.07849 6.72534 0.219002C6.95365 -0.00906182 7.16121 -0.00781147 7.39403 0.221003C7.54257 0.367044 7.69161 0.512585 7.83915 0.659376C8.05321 0.872686 8.05446 1.0945 7.83915 1.31031C6.96516 2.18556 6.09116 3.0603 5.21416 3.9323C5.14239 4.00382 5.13689 4.03958 5.21266 4.11485C6.08516 4.97934 6.9534 5.84833 7.8229 6.71583C8.06171 6.95415 8.06096 7.1607 7.82115 7.40077C7.67385 7.54806 7.52756 7.6961 7.37952 7.84265C7.16821 8.05145 6.94815 8.05145 6.73784 7.8414C5.86284 6.9674 4.98735 6.09415 4.1161 5.21641C4.04008 5.13989 4.00582 5.14739 3.93455 5.21941C3.07131 6.08765 2.20457 6.9524 1.33832 7.81789C1.09625 8.05971 0.893197 8.06021 0.65338 7.82089C0.51184 7.6796 0.3703 7.53831 0.22926 7.39677C-0.00555557 7.16145 -0.00705598 6.9529 0.225259 6.71933C1.0915 5.84909 1.95725 4.97784 2.82674 4.1106C2.89976 4.03783 2.90326 4.00232 2.82924 3.92855C1.94299 3.04805 1.06074 2.1638 0.177496 1.2803C-0.0575702 1.04523 -0.0590706 0.835926 0.172495 0.604111C0.328538 0.447817 0.485583 0.292773 0.640876 0.135979C0.723149 0.0524555 0.820177 0.00394182 0.912203 -5.93063e-05Z" fill="#263238"/> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.4 KiB | 
| @@ -5,6 +5,7 @@ import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileB | ||||
| import { Footer } from "../../components/Footer/Footer"; | ||||
| import { Navigation } from "../../components/Navigation/Navigation"; | ||||
| import { Loader } from "../../components/Loader/Loader"; | ||||
| import { urlForLocal } from '../../helper' | ||||
|  | ||||
| import { useDispatch, useSelector } from "react-redux"; | ||||
| import { apiRequest } from "../../api/request"; | ||||
| @@ -18,6 +19,7 @@ import { | ||||
|   activeLoader, | ||||
|   setColumnName, | ||||
|   setColumnId, | ||||
|   deletePersonOnProject | ||||
| } from "../../redux/projectsTrackerSlice"; | ||||
|  | ||||
| import ModalTicket from "../../components/UI/ModalTicket/ModalTicket"; | ||||
| @@ -32,6 +34,7 @@ import filesBoard from "../../images/filesBoard.svg"; | ||||
| import arrow from "../../images/arrowCalendar.png"; | ||||
| import del from "../../images/delete.svg"; | ||||
| import edit from "../../images/edit.svg"; | ||||
| import close from "../../images/closeProjectPersons.svg" | ||||
|  | ||||
| export const ProjectTracker = () => { | ||||
|   const dispatch = useDispatch(); | ||||
| @@ -44,6 +47,7 @@ export const ProjectTracker = () => { | ||||
|   const [modalAdd, setModalAdd] = useState(false); | ||||
|   const [modalActiveTicket, setModalActiveTicket] = useState(false); | ||||
|   const [selectedTicket, setSelectedTicket] = useState({}); | ||||
|   const [personListOpen, setPersonListOpen] = useState(false) | ||||
|  | ||||
|   const startWrapperIndexTest = useRef({}); | ||||
|   const projectBoard = useSelector(getProjectBoard); | ||||
| @@ -151,6 +155,18 @@ export const ProjectTracker = () => { | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   function deletePerson(userId) { | ||||
|     apiRequest("/project/del-user", { | ||||
|       method: "DELETE", | ||||
|       data: { | ||||
|         project_id: projectBoard.id, | ||||
|         user_id: userId | ||||
|       }, | ||||
|     }).then((res) => { | ||||
|       dispatch(deletePersonOnProject(userId)) | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <div className="tracker"> | ||||
|       <ProfileHeader /> | ||||
| @@ -226,13 +242,39 @@ export const ProjectTracker = () => { | ||||
|                     <span | ||||
|                       className="addPerson" | ||||
|                       onClick={() => { | ||||
|                         dispatch(modalToggle("addWorker")); | ||||
|                         setModalAdd(true); | ||||
|                         setPersonListOpen(true) | ||||
|                       }} | ||||
|                     > | ||||
|                       + | ||||
|                     </span> | ||||
|                     <p>добавить участника</p> | ||||
|                     {personListOpen && | ||||
|                     <div className='persons__list'> | ||||
|                       <img className='persons__list__close' src={close} alt='close' onClick={() => setPersonListOpen(false)} /> | ||||
|                       <div className='persons__list__count'><span>{projectBoard.projectUsers?.length}</span>участник</div> | ||||
|                       <div className='persons__list__info'>В проекте - <span>“{projectBoard.name}”</span></div> | ||||
|                       <div className='persons__list__items'> | ||||
|                         {projectBoard.projectUsers?.map((person) => { | ||||
|                           return <div className='persons__list__item' key={person.user_id}> | ||||
|                                     <img className='avatar' src={urlForLocal(person.user.avatar)} alt='avatar' /> | ||||
|                                     <span>{person.user.fio}</span> | ||||
|                                     <img className='delete' src={close} alt='delete' onClick={() => deletePerson(person.user_id)}/> | ||||
|                                 </div> | ||||
|                         }) | ||||
|                         } | ||||
|                       </div> | ||||
|                       <div className='persons__list__add' | ||||
|                            onClick={() => { | ||||
|                              dispatch(modalToggle("addWorker")); | ||||
|                              setModalAdd(true); | ||||
|                              setPersonListOpen(false) | ||||
|                            }} | ||||
|                       > | ||||
|                         <span className='addPerson'>+</span> | ||||
|                         <p>Добавить участников</p> | ||||
|                       </div> | ||||
|                     </div> | ||||
|                     } | ||||
|                   </div> | ||||
|                   <div className="tasks__head__select"> | ||||
|                     <span>Участвую</span> | ||||
|   | ||||
| @@ -309,6 +309,118 @@ | ||||
|               line-height: 17px; | ||||
|               max-width: 125px; | ||||
|             } | ||||
|  | ||||
|             .persons__list { | ||||
|               position: absolute; | ||||
|               z-index: 2; | ||||
|               display: flex; | ||||
|               flex-direction: column; | ||||
|               background: linear-gradient(180deg, #FFFFFF 0%, #EBEBEB 100%); | ||||
|               border-radius: 40px; | ||||
|               padding: 33px 24px 44px 34px; | ||||
|               width: 425px; | ||||
|               cursor: default; | ||||
|  | ||||
|               &__close { | ||||
|                 cursor: pointer; | ||||
|                 width: 8px; | ||||
|                 height: 8px; | ||||
|                 margin-left: auto; | ||||
|               } | ||||
|  | ||||
|               &__count { | ||||
|                 display: flex; | ||||
|                 align-items: end; | ||||
|                 color: #1458DD; | ||||
|                 font-size: 22px; | ||||
|                 margin-top: 10px; | ||||
|                 span { | ||||
|                   font-size: 44px; | ||||
|                   font-weight: 700; | ||||
|                   line-height: 40px; | ||||
|                   width: auto; | ||||
|                   height: auto; | ||||
|                   margin-right: 5px; | ||||
|                 } | ||||
|               } | ||||
|  | ||||
|               &__info { | ||||
|                 display: flex; | ||||
|                 font-size: 18px; | ||||
|                 line-height: 22px; | ||||
|                 color: #263238; | ||||
|                 font-weight: 500; | ||||
|                 margin: 13px 0 32px; | ||||
|  | ||||
|                 span { | ||||
|                   width: auto; | ||||
|                   height: auto; | ||||
|                   color: #1458DD; | ||||
|                   font-weight: 600; | ||||
|                   font-size: 18px; | ||||
|                   line-height: 22px; | ||||
|                 } | ||||
|               } | ||||
|  | ||||
|               &__items { | ||||
|                 display: flex; | ||||
|                 flex-wrap: wrap; | ||||
|                 row-gap: 60px; | ||||
|                 column-gap: 35px; | ||||
|                 margin-bottom: 38px; | ||||
|               } | ||||
|  | ||||
|               &__item { | ||||
|                 width: 145px; | ||||
|                 display: flex; | ||||
|                 justify-content: space-between; | ||||
|                 align-items: center; | ||||
|  | ||||
|                 .avatar { | ||||
|                   width: 22px; | ||||
|                   height: 22px; | ||||
|                 } | ||||
|  | ||||
|                 span { | ||||
|                   display: block; | ||||
|                   font-weight: 400; | ||||
|                   font-size: 12px; | ||||
|                   line-height: initial; | ||||
|                   color: #807777; | ||||
|                   width: auto; | ||||
|                   height: auto; | ||||
|                   max-width: 85px; | ||||
|                   overflow: hidden; | ||||
|                   white-space: nowrap; | ||||
|                   text-overflow: ellipsis; | ||||
|                 } | ||||
|  | ||||
|                 .delete { | ||||
|                   cursor: pointer; | ||||
|                   width: 14px; | ||||
|                   height: 14px; | ||||
|                 } | ||||
|               } | ||||
|  | ||||
|               &__add { | ||||
|                 display: flex; | ||||
|                 cursor: pointer; | ||||
|  | ||||
|                 span { | ||||
|                   background: #8BCC60; | ||||
|                   left: 0; | ||||
|                 } | ||||
|  | ||||
|                 p { | ||||
|                   margin-left: 17px; | ||||
|                   color: #000000; | ||||
|                   font-weight: 400; | ||||
|                   font-size: 12px; | ||||
|                   line-height: 32px; | ||||
|                   position: initial; | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           &__select { | ||||
|   | ||||
| @@ -35,6 +35,12 @@ export const projectsTrackerSlice = createSlice({ | ||||
|         } | ||||
|       }); | ||||
|     }, | ||||
|     deletePersonOnProject: (state,action) => { | ||||
|       state.projectBoard.projectUsers =  state.projectBoard.projectUsers.filter((person) => person.user_id !== action.payload) | ||||
|     }, | ||||
|     addPersonToProject: (state, action) => { | ||||
|       state.projectBoard.projectUsers.push(action.payload) | ||||
|     }, | ||||
|     activeLoader: (state) => { | ||||
|       state.boardLoader = true; | ||||
|     }, | ||||
| @@ -100,7 +106,9 @@ export const { | ||||
|   activeLoader, | ||||
|   editProjectName, | ||||
|   editColumnName, | ||||
|   setColumnId | ||||
|   setColumnId, | ||||
|   deletePersonOnProject, | ||||
|   addPersonToProject | ||||
| } = projectsTrackerSlice.actions; | ||||
|  | ||||
| export const getProjects = (state) => state.tracker.projects; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 NikoM1k
					NikoM1k