515 lines
17 KiB
JavaScript
515 lines
17 KiB
JavaScript
import React, {useEffect, useState} from "react";
|
||
import { Link } from "react-router-dom";
|
||
import TrackerModal from "../TrackerModal/TrackerModal";
|
||
import { apiRequest } from "../../../api/request";
|
||
import { useDispatch } from "react-redux";
|
||
import {
|
||
modalToggle,
|
||
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";
|
||
import arrow from "../../../images/arrowStart.png";
|
||
import link from "../../../images/link.svg";
|
||
import archive from "../../../images/archive.svg";
|
||
import del from "../../../images/delete.svg";
|
||
import edit from "../../../images/edit.svg";
|
||
import send from "../../../images/send.svg";
|
||
import plus from "../../../images/plus.svg";
|
||
import fullScreen from "../../../images/inFullScreen.svg";
|
||
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,
|
||
setActive,
|
||
task,
|
||
projectId,
|
||
projectName,
|
||
projectUsers
|
||
}) => {
|
||
const dispatch = useDispatch();
|
||
const [addSubtask, setAddSubtask] = useState(false);
|
||
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)
|
||
const [members, setMembers] = useState(task.taskUsers)
|
||
const [users, setUsers] = useState([])
|
||
const [timerStart, setTimerStart] = useState(false)
|
||
const [timerInfo, setTimerInfo] = useState({})
|
||
const [currentTimerCount, setCurrentTimerCount] = useState({
|
||
hours: 0,
|
||
minute: 0,
|
||
seconds: 0
|
||
})
|
||
const [timerId, setTimerId] = useState(null)
|
||
|
||
function deleteTask() {
|
||
apiRequest("/task/update-task", {
|
||
method: "PUT",
|
||
data: {
|
||
task_id: task.id,
|
||
status: 0,
|
||
},
|
||
}).then((res) => {
|
||
setActive(false);
|
||
dispatch(setProjectBoardFetch(projectId));
|
||
});
|
||
}
|
||
|
||
function editTask() {
|
||
apiRequest("/task/update-task", {
|
||
method: "PUT",
|
||
data: {
|
||
task_id: task.id,
|
||
title: inputsValue.title,
|
||
description: inputsValue.description
|
||
},
|
||
}).then((res) => {
|
||
dispatch(setProjectBoardFetch(projectId));
|
||
});
|
||
}
|
||
|
||
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) => {
|
||
// createSubComment()
|
||
})
|
||
}
|
||
|
||
// 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",
|
||
data: {
|
||
entity_type: 2,
|
||
entity_id: task.id,
|
||
created_at: getCorrectRequestDate(new Date())
|
||
}
|
||
}).then((res) => {
|
||
setTimerStart(true)
|
||
setTimerInfo(res)
|
||
startTimer()
|
||
})
|
||
}
|
||
|
||
function stopTaskTimer() {
|
||
apiRequest("/timer/update", {
|
||
method: "PUT",
|
||
data: {
|
||
timer_id: timerInfo.id,
|
||
stopped_at: getCorrectRequestDate(new Date())
|
||
}
|
||
}).then((res) => {
|
||
setTimerStart(false)
|
||
clearInterval(timerId)
|
||
})
|
||
}
|
||
|
||
function taskExecutor(person) {
|
||
apiRequest("/task/update-task", {
|
||
method: "PUT",
|
||
data: {
|
||
task_id: task.id,
|
||
executor_id: person.user_id
|
||
},
|
||
}).then((res) => {
|
||
setDropListOpen(false)
|
||
setExecutor(res.executor)
|
||
});
|
||
}
|
||
|
||
function deleteTaskExecutor() {
|
||
apiRequest("/task/update-task", {
|
||
method: "PUT",
|
||
data: {
|
||
task_id: task.id,
|
||
executor_id: 0
|
||
},
|
||
}).then((res) => {
|
||
setExecutor(null)
|
||
});
|
||
}
|
||
|
||
function addMember(person) {
|
||
apiRequest("/task/add-user-to-task", {
|
||
method: "POST",
|
||
data: {
|
||
task_id: task.id,
|
||
user_id: person.user_id
|
||
},
|
||
}).then((res) => {
|
||
setDropListMembersOpen(false)
|
||
setMembers((prevValue) => ([...prevValue, res]))
|
||
});
|
||
}
|
||
|
||
function deleteMember(person) {
|
||
apiRequest("/task/del-user", {
|
||
method: "DELETE",
|
||
data: {
|
||
task_id: task.id,
|
||
user_id: person.user_id
|
||
},
|
||
}).then((res) => {
|
||
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}))
|
||
})
|
||
})
|
||
apiRequest(`/timer/get-by-entity?entity_type=2&entity_id=${task.id}`).then((res) => {
|
||
let timerSeconds = 0
|
||
res.forEach((time) => {
|
||
timerSeconds += time.deltaSeconds
|
||
setCurrentTimerCount({
|
||
hours: Math.floor(timerSeconds / 60 / 60),
|
||
minute: Math.floor(timerSeconds / 60 % 60),
|
||
seconds: timerSeconds % 60}
|
||
)
|
||
updateTimerHours = Math.floor(timerSeconds / 60 / 60)
|
||
updateTimerMinute = Math.floor(timerSeconds / 60 % 60)
|
||
updateTimerSec = timerSeconds % 60
|
||
if (!time.stopped_at) {
|
||
setTimerStart(true)
|
||
startTimer()
|
||
setTimerInfo(time)
|
||
}
|
||
})
|
||
})
|
||
}, [])
|
||
|
||
function startTimer () {
|
||
setTimerId(setInterval(() => {
|
||
run()
|
||
}, 1000))
|
||
}
|
||
|
||
let updateTimerSec = currentTimerCount.seconds, updateTimerMinute = currentTimerCount.minute, updateTimerHours = currentTimerCount.hours
|
||
|
||
function run () {
|
||
updateTimerSec++
|
||
if (updateTimerSec > 60) {
|
||
updateTimerMinute++
|
||
updateTimerSec = 0
|
||
}
|
||
if (updateTimerMinute === 60) {
|
||
updateTimerMinute = 0
|
||
updateTimerHours++
|
||
}
|
||
|
||
return setCurrentTimerCount({
|
||
hours: updateTimerHours,
|
||
minute: updateTimerMinute,
|
||
seconds: updateTimerSec
|
||
})
|
||
}
|
||
|
||
function correctTimerTime (time) {
|
||
if (time < 10) return `0${time}`
|
||
if (time > 10) return time
|
||
}
|
||
|
||
|
||
useEffect(() => {
|
||
let ids = members.map((user) => user.user_id)
|
||
setUsers(projectUsers.reduce((acc, cur) => {
|
||
if (!ids.includes(cur.user_id)) acc.push(cur)
|
||
return acc
|
||
}, []))
|
||
}, [members])
|
||
|
||
|
||
return (
|
||
<div
|
||
className={active ? "modal-tiket active" : "modal-tiket"}
|
||
onClick={() => setActive(false)}
|
||
>
|
||
<div
|
||
className="modal-tiket__content"
|
||
onClick={(e) => e.stopPropagation()}
|
||
>
|
||
<div className="content">
|
||
<h3 className="title-project">
|
||
<img src={category} className="title-project__category"></img>
|
||
Проект: {projectName}
|
||
<Link
|
||
to={`/tracker/task/${task.id}`}
|
||
className="title-project__full"
|
||
>
|
||
<img src={fullScreen}></img>
|
||
</Link>
|
||
</h3>
|
||
|
||
<div className="content__task">
|
||
<span>Задача</span>
|
||
{editOpen ? <input value={inputsValue.title} onChange={(e) => {
|
||
setInputsValue((prevValue) => ({...prevValue, title: e.target.value}))
|
||
}} /> :<h5>{inputsValue.title}</h5>}
|
||
<div className="content__description">
|
||
{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>*/}
|
||
</div>
|
||
<div className="content__communication">
|
||
<p className="tasks">
|
||
<button
|
||
onClick={() => {
|
||
dispatch(modalToggle("addSubtask"));
|
||
setAddSubtask(true);
|
||
}}
|
||
>
|
||
<img src={plus}></img>
|
||
Добавить под задачу
|
||
</button>
|
||
</p>
|
||
<p className="file">
|
||
<button>
|
||
<img src={file}></img>
|
||
Загрузить файл
|
||
</button>
|
||
<span>{0}</span>
|
||
Файлов
|
||
</p>
|
||
</div>
|
||
<div className="content__input">
|
||
<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'>
|
||
<div className='comments__list__item__fio'>
|
||
<img src={urlForLocal(comment.user.avatar)} alt='avatar' />
|
||
<p>{comment.user.fio}</p>
|
||
</div>
|
||
<div className='comments__list__item__date'>
|
||
<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>
|
||
</div>
|
||
{commentsEditOpen[comment.id] ? <input className='comments__list__item__text' value={commentsEditText[comment.id]} onChange={(e) => {
|
||
setCommentsEditText((prevValue) => ({...prevValue, [comment.id]: e.target.value}))
|
||
}} /> : <p className='comments__list__item__text'>{commentsEditText[comment.id]}</p>}
|
||
{subCommentsCreateOpen[comment.id] ?
|
||
<div className='comments__list__item__answer__new'>
|
||
<input />
|
||
<img src={accept} alt='accept'
|
||
onClick={() => {
|
||
setSubCommentsCreateOpen((prevValue) => ({...prevValue, [comment.id]: !prevValue[comment.id]}))
|
||
}}
|
||
/>
|
||
</div>
|
||
:
|
||
<span onClick={() => {
|
||
setSubCommentsCreateOpen((prevValue) => ({...prevValue, [comment.id]: !prevValue[comment.id]}))
|
||
}} className='comments__list__item__answer'>Ответить</span>
|
||
}
|
||
</div>
|
||
})
|
||
|
||
}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="workers">
|
||
<div className="workers_box task__info">
|
||
<span className="exit" onClick={() => setActive(false)}></span>
|
||
<span className='nameProject'>{task.title}</span>
|
||
<p className="workers__creator">Создатель : {task.user?.fio}</p>
|
||
|
||
{executor ?
|
||
<div className='executor'>
|
||
<p>Исполнитель: {executor.fio}</p>
|
||
<img src={urlForLocal(executor.avatar)} alt='avatar' />
|
||
<img src={close} className='delete' onClick={() => deleteTaskExecutor()} />
|
||
</div> :
|
||
<div className="add-worker moreItems ">
|
||
<button onClick={() => setDropListOpen(true)}>+</button>
|
||
<span>Добавить исполнителя</span>
|
||
{dropListOpen &&
|
||
<div className='dropdownList'>
|
||
<img src={close} className='dropdownList__close' onClick={() => setDropListOpen(false)} />
|
||
{projectUsers.map((person) => {
|
||
return <div className='dropdownList__person' key={person.user_id} onClick={() => taskExecutor(person)}>
|
||
<span>{person.user.fio}</span>
|
||
<img src={urlForLocal(person.user.avatar)} />
|
||
</div>
|
||
})
|
||
}
|
||
</div>
|
||
}
|
||
</div>
|
||
}
|
||
|
||
{Boolean(members.length) &&
|
||
<div className='members'>
|
||
<p>Участники:</p>
|
||
<div className='members__list'>
|
||
{members.map((member) => {
|
||
return <div className='worker' key={member.user_id}>
|
||
<p>{member.fio}</p>
|
||
<img src={urlForLocal(member.avatar)} />
|
||
<img src={close} className='delete' onClick={() => deleteMember(member)} />
|
||
</div>
|
||
})
|
||
}
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
<div className="add-worker moreItems">
|
||
<button onClick={() => setDropListMembersOpen(true)}>+</button>
|
||
<span>Добавить участников</span>
|
||
{dropListMembersOpen &&
|
||
<div className='dropdownList'>
|
||
<img src={close} className='dropdownList__close' onClick={() => setDropListMembersOpen(false)} />
|
||
{users.length ? users.map((person) => {
|
||
return <div className='dropdownList__person' key={person.user_id} onClick={() => addMember(person)}>
|
||
<span>{person.user.fio}</span>
|
||
<img src={urlForLocal(person.user.avatar)} />
|
||
</div>
|
||
}) : <p className='noUsers'>Нет пользователей</p>
|
||
}
|
||
</div>
|
||
}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="workers_box-middle">
|
||
<div className="time">
|
||
<img src={watch}></img>
|
||
<span>Длительность : </span>
|
||
<p>
|
||
{correctTimerTime(currentTimerCount.hours)}:{correctTimerTime(currentTimerCount.minute)}:{correctTimerTime(currentTimerCount.seconds)}
|
||
</p>
|
||
</div>
|
||
|
||
{timerStart ?
|
||
<button className="stop" onClick={() => stopTaskTimer()}>
|
||
Остановить
|
||
</button>
|
||
:
|
||
<button className="start" onClick={() => startTaskTimer()}>
|
||
Начать делать <img src={arrow}></img>
|
||
</button>
|
||
}
|
||
</div>
|
||
|
||
<div className="workers_box-bottom">
|
||
<div className={editOpen ? 'edit' : ''} onClick={() => {
|
||
if(editOpen) {
|
||
setEditOpen(!editOpen)
|
||
editTask()
|
||
} else {
|
||
setEditOpen(!editOpen)
|
||
}
|
||
}}>
|
||
<img src={edit}></img>
|
||
<p>{editOpen ? 'сохранить' : 'редактировать'}</p>
|
||
</div>
|
||
<div>
|
||
<img src={link}></img>
|
||
<p>ссылка на проект</p>
|
||
</div>
|
||
<div onClick={deleteTask}>
|
||
<img src={archive}></img>
|
||
<p>в архив</p>
|
||
</div>
|
||
<div onClick={deleteTask}>
|
||
<img src={del}></img>
|
||
<p>удалить</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<TrackerModal
|
||
active={addSubtask}
|
||
setActive={setAddSubtask}
|
||
defautlInput={task.column_id}
|
||
></TrackerModal>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default ModalTiсket;
|