import ClassicEditor from "@ckeditor/ckeditor5-build-classic"; import { CKEditor } from "@ckeditor/ckeditor5-react"; import ru from "date-fns/locale/ru"; import React, { useEffect, useState } from "react"; import DatePicker, { registerLocale } from "react-datepicker"; import { useDispatch, useSelector } from "react-redux"; import { Link, useNavigate, useParams } from "react-router-dom"; import { getProfileInfo } from "@redux/outstaffingSlice"; import { getBoarderLoader, setToggleTab } from "@redux/projectsTrackerSlice"; import { backendImg, caseOfNum, getCorrectRequestDate, getToken, urlForLocal } from "@utils/helper"; import { apiRequest } from "@api/request"; import { useNotification } from "@hooks/useNotification"; import { getCorrectDate } from "@utils/calendarHelper"; import { Footer } from "@components/Common/Footer/Footer"; import { Loader } from "@components/Common/Loader/Loader"; import FileTracker from "@components/FileTracker/FileTracker"; import AcceptModal from "@components/Modal/AcceptModal/AcceptModal"; import { Navigation } from "@components/Navigation/Navigation"; import { ProfileBreadcrumbs } from "@components/ProfileBreadcrumbs/ProfileBreadcrumbs"; import { ProfileHeader } from "@components/ProfileHeader/ProfileHeader"; import TrackerTaskComment from "@components/TrackerTaskComment/TrackerTaskComment"; import arrow from "assets/icons/arrows/arrowCalendar.png"; import arrowStart from "assets/icons/arrows/arrowStart.png"; import arrowDown from "assets/icons/arrows/selectArrow.png"; import calendarIcon from "assets/icons/calendar.svg"; import close from "assets/icons/close.png"; import fileDelete from "assets/icons/closeProjectPersons.svg"; import del from "assets/icons/delete.svg"; import edit from "assets/icons/edit.svg"; import file from "assets/icons/fileModal.svg"; import link from "assets/icons/link.svg"; import send from "assets/icons/send.svg"; import project from "assets/icons/trackerProject.svg"; import tasks from "assets/icons/trackerTasks.svg"; import watch from "assets/icons/watch.svg"; import archive from "assets/images/archiveIcon.png"; import avatarMok from "assets/images/avatarMok.png"; import "./ticketFullScreen.scss"; registerLocale("ru", ru); export const TicketFullScreen = () => { const ticketId = useParams(); const dispatch = useDispatch(); const navigate = useNavigate(); const boardLoader = useSelector(getBoarderLoader); const [projectInfo, setProjectInfo] = useState({}); const [taskInfo, setTaskInfo] = useState({}); const [editOpen, setEditOpen] = useState(false); const [inputsValue, setInputsValue] = useState({}); const [loader, setLoader] = useState(true); const [comments, setComments] = useState([]); const [timerStart, setTimerStart] = useState(false); const [timerInfo, setTimerInfo] = useState({}); const [currentTimerCount, setCurrentTimerCount] = useState({ hours: 0, minute: 0, seconds: 0 }); const profileInfo = useSelector(getProfileInfo); const [timerId, setTimerId] = useState(null); const [dropListOpen, setDropListOpen] = useState(false); const [correctProjectUsers, setCorrectProjectUsers] = useState([]); const [dropListMembersOpen, setDropListMembersOpen] = useState(false); const [users, setUsers] = useState([]); const [deadLine, setDeadLine] = useState(""); const [datePickerOpen, setDatePickerOpen] = useState(false); const [startDate, setStartDate] = useState(null); const [uploadedFile, setUploadedFile] = useState(null); const [taskFiles, setTaskFiles] = useState([]); const [taskPriority, setTaskPriority] = useState(""); const [acceptModalOpen, setAcceptModalOpen] = useState(false); const [taskTags, setTaskTags] = useState([]); const [selectTagsOpen, setSelectTagsOpen] = useState(false); const [selectPriorityOpen, setSelectPriorityOpen] = useState(false); const [correctProjectTags, setCorrectProjectTags] = useState([]); const { showNotification } = useNotification(); const [commentSendDisable, setCommentSendDisable] = useState(false); const priority = { 2: "Высокий", 1: "Средний", 0: "Низкий" }; const priorityTypes = [ { name: "Высокий", key: 2 }, { name: "Средний", key: 1 }, { name: "Низкий", key: 0 } ]; useEffect(() => { initListeners(); apiRequest(`/task/get-task?task_id=${ticketId.id}&expand=mark`).then( (taskInfo) => { setTaskInfo(taskInfo); setDeadLine(taskInfo.dead_line); setTaskPriority(taskInfo.execution_priority); setStartDate( taskInfo.dead_line ? new Date(taskInfo.dead_line) : new Date() ); setInputsValue({ title: taskInfo.title, description: taskInfo.description, comment: "" }); setTaskTags(taskInfo.mark); apiRequest( `/comment/get-by-entity?entity_type=2&entity_id=${taskInfo.id}` ).then((res) => { 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( `/file/get-by-entity?entity_type=2&entity_id=${taskInfo.id}` ).then((res) => { if (Array.isArray(res)) { setTaskFiles(res); } }); apiRequest( `/timer/get-by-entity?entity_type=2&entity_id=${taskInfo.id}` ).then((res) => { let timerSeconds = 0; res.length && 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); } }); }); apiRequest( `/project/get-project?project_id=${taskInfo.project_id}&expand=columns,mark` ).then((res) => { setProjectInfo(res); setCorrectProjectUsers(res.projectUsers); }); setLoader(boardLoader); } ); }, []); useEffect(() => { let tagIds = taskTags.map((tag) => tag.id); if (projectInfo.mark) { setCorrectProjectTags( projectInfo.mark.reduce((acc, cur) => { if (!tagIds.includes(cur.id)) acc.push(cur); return acc; }, []) ); } }, [taskTags, projectInfo]); function deleteTask() { apiRequest("/task/update-task", { method: "PUT", data: { task_id: ticketId.id, status: 0 } }).then(() => { navigate(`/tracker/project/${taskInfo.project_id}`); }); } function archiveTask() { setAcceptModalOpen(true); } function editTask() { if (!inputsValue.title || !inputsValue.description) { return showNotification({ show: true, text: "Заполните поля", type: "error" }); } apiRequest("/task/update-task", { method: "PUT", data: { task_id: taskInfo.id, title: inputsValue.title, description: inputsValue.description } }).then(() => { showNotification({ show: true, text: "Изменения сохранены", type: "success" }); setEditOpen(!editOpen); }); } function createComment() { if (!inputsValue.comment) return; setCommentSendDisable(true); apiRequest("/comment/create", { method: "POST", data: { text: inputsValue.comment, entity_type: 2, entity_id: taskInfo.id } }).then((res) => { let newComment = res; setCommentSendDisable(false); newComment.created_at = new Date(); newComment.subComments = []; setInputsValue((prevValue) => ({ ...prevValue, comment: "" })); setComments((prevValue) => [...prevValue, newComment]); }); } function startTaskTimer() { apiRequest("/timer/create", { method: "POST", data: { entity_type: 2, entity_id: taskInfo.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(() => { setTimerStart(false); clearInterval(timerId); }); } 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)); }; function copyTicketLink() { navigator.clipboard.writeText( `https://itguild.info/tracker/task/${taskInfo.id}` ); } function startTimer() { setTimerId( setInterval(() => { run(); }, 1000) ); } useEffect(() => { if (taskInfo.taskUsers && projectInfo.projectUsers) { let ids = taskInfo.taskUsers.map((user) => user.user_id); setUsers( projectInfo.projectUsers.reduce((acc, cur) => { if (!ids.includes(cur.user_id)) acc.push(cur); return acc; }, []) ); } }, [taskInfo.taskUsers, projectInfo]); 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; } function deleteTaskExecutor() { apiRequest("/task/update-task", { method: "PUT", data: { task_id: taskInfo.id, executor_id: 0 } }).then(() => { setTaskInfo((prevState) => ({ ...prevState, executor_id: null, executor: null })); }); } function taskExecutor(person) { apiRequest("/task/update-task", { method: "PUT", data: { task_id: taskInfo.id, executor_id: person.user_id } }).then((res) => { setDropListOpen(false); setTaskInfo((prevState) => ({ ...prevState, executor_id: res.executor_id, executor: res.executor })); }); } function deleteMember(person) { apiRequest("/task/del-user", { method: "DELETE", data: { task_id: taskInfo.id, user_id: person.user_id } }).then(() => { setTaskInfo((prevState) => ({ ...prevState, taskUsers: taskInfo.taskUsers.filter( (item) => item.user_id !== person.user_id ) })); }); } function addMember(person) { apiRequest("/task/add-user-to-task", { method: "POST", data: { task_id: taskInfo.id, user_id: person.user_id } }).then((res) => { setDropListMembersOpen(false); setTaskInfo((prevValue) => ({ ...prevValue, taskUsers: [...prevValue.taskUsers, res] })); }); } function selectDeadLine(date) { apiRequest("/task/update-task", { method: "PUT", data: { task_id: taskInfo.id, dead_line: getCorrectRequestDate(date) } }).then(() => {}); } async function handleUpload(event) { const formData = new FormData(); formData.append("uploadFile", event.target.files[0]); const res = await fetch("https://itguild.info/api/file/upload", { method: "POST", body: formData, headers: { ...getToken() } }); const data = await res.json(); setUploadedFile(data); } function attachFile() { apiRequest("/file/attach", { method: "POST", data: { file_id: uploadedFile[0].id, entity_type: 2, entity_id: taskInfo.id, status: 1 } }).then((res) => { setTaskFiles((prevValue) => [...prevValue, res]); setUploadedFile(null); }); } function deleteLoadedFile() { setUploadedFile(null); } function updateTaskPriority(key) { setSelectPriorityOpen(false); apiRequest("/task/update-task", { method: "PUT", data: { task_id: taskInfo.id, execution_priority: key } }).then(() => {}); } // function deleteFile(file) { // apiRequest("/file/detach", { // method: "DELETE", // data: { // file_id: file.id, // entity_type: 2, // entity_id: taskInfo.id, // status: 0, // }, // }).then(() => { // setTaskFiles((prevValue) => // prevValue.filter((item) => item.id !== file.id) // ); // }); // } function deleteFile(file) { setTaskFiles((prevValue) => prevValue.filter((item) => item.id !== file.id) ); } function closeAcceptModal() { setAcceptModalOpen(false); } function deleteTagFromTask(tagId) { apiRequest("/mark/detach", { method: "DELETE", data: { mark_id: tagId, entity_type: 2, entity_id: taskInfo.id } }).then(() => { setTaskTags((prevValue) => prevValue.filter((tag) => tag.id !== tagId)); }); } function addTagToTask(tagId) { apiRequest("/mark/attach", { method: "POST", data: { mark_id: tagId, entity_type: 2, entity_id: taskInfo.id } }).then((data) => { setSelectTagsOpen(false); setTaskTags((prevValue) => [...prevValue, data.mark]); }); } const initListeners = () => { document.addEventListener("click", closeByClickingOut); }; const closeByClickingOut = (event) => { const path = event.path || (event.composedPath && event.composedPath()); if ( event && !path.find( (div) => div.classList && (div.classList.contains("button-add-worker") || div.classList.contains("dropdown-list")) ) ) { setDropListOpen(false); setDropListMembersOpen(false); } if ( event && !path.find( (div) => div.classList && (div.classList.contains("dead-line") || div.classList.contains("react-datepicker-popper")) ) ) { setDatePickerOpen(false); } if ( event && !path.find( (div) => div.classList && (div.classList.contains("tags") || div.classList.contains("tags__dropDown")) ) ) { setSelectTagsOpen(false); } }; return (

Управление проектами с трекером

toggleTabs(1)} > img

Проекты

toggleTabs(2)} > img

Все мои задачи

toggleTabs(3)} > img

Архив

{loader ? ( ) : ( <>
{projectInfo.name}

К списку задач

arrow
{editOpen ? ( { setInputsValue((prevValue) => ({ ...prevValue, title: e.target.value })); }} /> ) : (
{inputsValue.title}
)}
{editOpen ? ( { const data = editor.getData(); setInputsValue((prevValue) => ({ ...prevValue, description: data })); }} /> ) : (
)}
{Boolean(taskFiles.length) && (
{taskFiles.map((file) => { return ( ); })}
)} {uploadedFile && (
{uploadedFile.map((file) => { return (
img
deleteLoadedFile(file)} > delete
); })}
)}
{taskFiles.length ? taskFiles.length : 0} {caseOfNum(taskFiles.length, "files")}
{ setInputsValue((prevValue) => ({ ...prevValue, comment: e.target.value })); }} /> send
{comments.map((comment) => { return ( ); })}
Создатель:

{taskInfo.user?.fio}

{taskInfo.executor ? ( <>
Исполнитель:

{taskInfo.executor.fio}

avatar deleteTaskExecutor()} />
) : (
Добавить исполнителя {dropListOpen && (
setDropListOpen(false)} /> {correctProjectUsers.map((person) => { return (
taskExecutor(person)} > {person.user.fio} avatar
); })}
)}
)} {Boolean(taskInfo.taskUsers.length) && (

Участники:

{taskInfo.taskUsers.map((member) => { return (

{member.fio}

avatar deleteMember(member)} />
); })}
)}
Добавить участника {dropListMembersOpen && (
setDropListMembersOpen(false)} /> {users.length ? ( users.map((person) => { return (
addMember(person)} > {person.user.fio} avatar
); }) ) : (

Нет пользователей

)}
)}
setDatePickerOpen(!datePickerOpen)} > calendar {deadLine ? getCorrectDate(deadLine) : "Срок исполнения:"}
{ setDatePickerOpen(false); setStartDate(date); setDeadLine(date); selectDeadLine(date); }} />

{correctTimerTime(currentTimerCount.hours)}: {correctTimerTime(currentTimerCount.minute)}: {correctTimerTime(currentTimerCount.seconds)}

{timerStart ? ( ) : ( )}
{taskTags.map((tag) => { return (

{tag.slug}

delete deleteTagFromTask(tag.id)} />
); })}
setSelectTagsOpen(!selectTagsOpen)} > Выберите тег arrow
{selectTagsOpen && (
setSelectTagsOpen(false)} className="tags__dropDown__close" src={close} alt="close" /> {correctProjectTags.map((tag) => { return (
addTagToTask(tag.id)} >

{tag.slug}

); })} {!Boolean(correctProjectTags.length) && (

Нет тегов

)}
)}
setSelectPriorityOpen(!selectPriorityOpen)} > {typeof taskPriority === "number" ? `Приоритет: ${priority[taskPriority]}` : "Выберите приоритет"} arrow
{selectPriorityOpen && (
{priorityTypes.map((item) => { return (
{ setTaskPriority(item.key); updateTaskPriority(item.key); }} > {item.name}
); })}
)}
{ if (editOpen) { editTask(); } else { setEditOpen(!editOpen); } }} > edit

{editOpen ? "сохранить" : "редактировать"}

link

ссылка на задачу

arch

в архив

delete

удалить

)}
{acceptModalOpen && ( )}
); }; export default TicketFullScreen;