1298 lines
43 KiB
React
Raw Normal View History

2023-06-29 02:27:08 +03:00
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
import { CKEditor } from "@ckeditor/ckeditor5-react";
2023-07-07 17:38:31 +03:00
import ru from "date-fns/locale/ru";
2023-05-04 18:38:56 +03:00
import React, { useEffect, useState } from "react";
2023-07-07 17:38:31 +03:00
import DatePicker, { registerLocale } from "react-datepicker";
2023-06-22 14:53:35 +03:00
import { useDispatch, useSelector } from "react-redux";
import { Link, useNavigate, useParams } from "react-router-dom";
2023-05-30 10:10:34 +03:00
2023-08-31 01:58:48 +03:00
import { getProfileInfo } from "@redux/outstaffingSlice";
2023-05-23 23:02:39 +03:00
import {
deletePersonOnProject,
2023-06-22 14:53:35 +03:00
getBoarderLoader,
2023-11-21 16:41:18 +03:00
modalToggle,
setProjectBoardFetch,
2023-06-29 03:41:59 +03:00
setToggleTab,
2023-06-22 14:53:35 +03:00
} from "@redux/projectsTrackerSlice";
2023-05-31 08:36:15 +03:00
2023-07-13 15:49:34 +03:00
import {
backendImg,
caseOfNum,
getCorrectRequestDate,
getToken,
urlForLocal,
} from "@utils/helper";
2023-04-20 20:10:08 +03:00
2023-06-22 14:53:35 +03:00
import { apiRequest } from "@api/request";
2023-08-30 17:02:50 +03:00
import { useNotification } from "@hooks/useNotification";
2023-07-07 17:38:31 +03:00
import { getCorrectDate } from "@components/Calendar/calendarHelper";
2023-06-22 14:53:35 +03:00
import { Footer } from "@components/Common/Footer/Footer";
import { Loader } from "@components/Common/Loader/Loader";
2023-11-19 20:01:27 +03:00
import FileTracker from "@components/FileTracker/FileTracker";
2023-07-14 03:03:49 +03:00
import AcceptModal from "@components/Modal/AcceptModal/AcceptModal";
2023-06-22 19:18:41 +03:00
import TrackerModal from "@components/Modal/Tracker/TrackerModal/TrackerModal";
2023-06-22 14:53:35 +03:00
import { Navigation } from "@components/Navigation/Navigation";
import { ProfileBreadcrumbs } from "@components/ProfileBreadcrumbs/ProfileBreadcrumbs";
import { ProfileHeader } from "@components/ProfileHeader/ProfileHeader";
import TrackerTaskComment from "@components/TrackerTaskComment/TrackerTaskComment";
2023-04-20 20:10:08 +03:00
2023-06-22 14:53:35 +03:00
import arrow from "assets/icons/arrows/arrowCalendar.png";
2023-06-29 02:26:49 +03:00
import arrowStart from "assets/icons/arrows/arrowStart.png";
2023-10-26 14:45:14 +03:00
import arrowDown from "assets/icons/arrows/selectArrow.png";
2023-07-07 17:38:31 +03:00
import calendarIcon from "assets/icons/calendar.svg";
2023-06-22 14:53:35 +03:00
import close from "assets/icons/close.png";
2023-07-13 15:49:07 +03:00
import fileDelete from "assets/icons/closeProjectPersons.svg";
2023-06-22 14:53:35 +03:00
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";
2023-07-07 00:46:58 +03:00
import archive from "assets/images/archiveIcon.png";
2023-06-29 02:27:08 +03:00
import avatarMok from "assets/images/avatarMok.png";
2023-06-12 23:30:18 +03:00
2023-06-22 14:53:35 +03:00
import "./ticketFullScreen.scss";
2023-07-07 17:38:31 +03:00
registerLocale("ru", ru);
2023-06-22 14:53:35 +03:00
export const TicketFullScreen = () => {
2023-04-20 20:10:08 +03:00
const [modalAddWorker, setModalAddWorker] = useState(false);
2023-05-04 18:38:56 +03:00
const ticketId = useParams();
2023-05-02 18:51:19 +03:00
const dispatch = useDispatch();
2023-05-03 20:01:23 +03:00
const navigate = useNavigate();
2023-05-23 23:02:39 +03:00
const boardLoader = useSelector(getBoarderLoader);
2023-06-29 03:41:59 +03:00
const [projectInfo, setProjectInfo] = useState({});
2023-05-04 18:38:56 +03:00
const [taskInfo, setTaskInfo] = useState({});
2023-05-16 00:24:52 +03:00
const [editOpen, setEditOpen] = useState(false);
2023-05-17 23:18:46 +03:00
const [inputsValue, setInputsValue] = useState({});
const [loader, setLoader] = useState(true);
const [comments, setComments] = useState([]);
2023-06-22 14:53:35 +03:00
const [personListOpen, setPersonListOpen] = useState(false);
const [timerStart, setTimerStart] = useState(false);
const [timerInfo, setTimerInfo] = useState({});
2023-06-29 02:26:49 +03:00
const [currentTimerCount, setCurrentTimerCount] = useState({
hours: 0,
minute: 0,
seconds: 0,
});
2023-08-31 01:58:35 +03:00
const profileInfo = useSelector(getProfileInfo);
2023-06-29 02:26:49 +03:00
const [timerId, setTimerId] = useState(null);
2023-06-29 03:41:18 +03:00
const [dropListOpen, setDropListOpen] = useState(false);
const [correctProjectUsers, setCorrectProjectUsers] = useState([]);
const [dropListMembersOpen, setDropListMembersOpen] = useState(false);
const [users, setUsers] = useState([]);
2023-07-07 17:38:31 +03:00
const [deadLine, setDeadLine] = useState("");
2023-07-07 17:38:19 +03:00
const [datePickerOpen, setDatePickerOpen] = useState(false);
const [startDate, setStartDate] = useState(null);
2023-07-13 15:49:07 +03:00
const [uploadedFile, setUploadedFile] = useState(null);
2023-07-13 15:49:34 +03:00
const [taskFiles, setTaskFiles] = useState([]);
2023-11-21 16:41:18 +03:00
const [taskPriority, setTaskPriority] = useState("");
2023-07-14 03:03:49 +03:00
const [acceptModalOpen, setAcceptModalOpen] = useState(false);
2023-10-26 14:44:39 +03:00
const [taskTags, setTaskTags] = useState([]);
const [selectTagsOpen, setSelectTagsOpen] = useState(false);
2023-11-21 16:41:18 +03:00
const [selectPriorityOpen, setSelectPriorityOpen] = useState(false);
2023-10-26 14:44:39 +03:00
const [correctProjectTags, setCorrectProjectTags] = useState([]);
2023-08-30 17:02:32 +03:00
const { showNotification } = useNotification();
2023-10-28 17:57:58 +03:00
const [commentSendDisable, setCommentSendDisable] = useState(false);
2023-04-20 20:10:08 +03:00
2023-11-21 16:40:58 +03:00
const priority = {
2023-11-21 16:41:18 +03:00
2: "Высокий",
1: "Средний",
0: "Низкий",
};
2023-11-21 16:40:58 +03:00
const priorityTypes = [
{
2023-11-21 16:41:18 +03:00
name: "Высокий",
key: 2,
2023-11-21 16:40:58 +03:00
},
{
2023-11-21 16:41:18 +03:00
name: "Средний",
key: 1,
2023-11-21 16:40:58 +03:00
},
{
2023-11-21 16:41:18 +03:00
name: "Низкий",
key: 0,
2023-11-21 16:40:58 +03:00
},
2023-11-21 16:41:18 +03:00
];
2023-11-21 16:40:58 +03:00
2023-05-03 20:01:23 +03:00
useEffect(() => {
2023-10-28 17:57:58 +03:00
initListeners();
2023-10-13 14:39:01 +03:00
apiRequest(`/task/get-task?task_id=${ticketId.id}&expand=mark`).then(
(taskInfo) => {
setTaskInfo(taskInfo);
setDeadLine(taskInfo.dead_line);
2023-11-21 16:41:18 +03:00
setTaskPriority(taskInfo.execution_priority);
2023-10-13 14:39:01 +03:00
setStartDate(
taskInfo.dead_line ? new Date(taskInfo.dead_line) : new Date()
);
setInputsValue({
title: taskInfo.title,
description: taskInfo.description,
comment: "",
});
2023-10-26 14:45:14 +03:00
setTaskTags(taskInfo.mark);
2023-10-13 14:39:01 +03:00
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);
2023-06-12 23:30:18 +03:00
}
2023-10-13 14:39:01 +03:00
});
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);
}
2023-06-29 02:26:49 +03:00
});
2023-10-13 14:39:01 +03:00
});
apiRequest(
2023-10-26 14:44:39 +03:00
`/project/get-project?project_id=${taskInfo.project_id}&expand=columns,mark`
2023-10-13 14:39:01 +03:00
).then((res) => {
setProjectInfo(res);
setCorrectProjectUsers(res.projectUsers);
});
setLoader(boardLoader);
}
);
2023-05-04 18:38:56 +03:00
}, []);
2023-05-03 20:01:23 +03:00
2023-10-26 14:44:39 +03:00
useEffect(() => {
let tagIds = taskTags.map((tag) => tag.id);
if (projectInfo.mark) {
setCorrectProjectTags(
2023-10-26 14:45:14 +03:00
projectInfo.mark.reduce((acc, cur) => {
if (!tagIds.includes(cur.id)) acc.push(cur);
return acc;
}, [])
2023-10-26 14:44:39 +03:00
);
}
}, [taskTags, projectInfo]);
2023-05-03 20:01:23 +03:00
function deleteTask() {
apiRequest("/task/update-task", {
method: "PUT",
data: {
task_id: ticketId.id,
status: 0,
},
2023-05-31 11:24:46 +03:00
}).then(() => {
2023-05-04 18:38:56 +03:00
navigate(`/tracker/project/${taskInfo.project_id}`);
2023-05-03 20:01:23 +03:00
});
}
2023-04-20 20:10:08 +03:00
2023-07-14 03:03:49 +03:00
function archiveTask() {
setAcceptModalOpen(true);
2023-07-14 03:03:33 +03:00
}
2023-05-16 00:24:52 +03:00
function editTask() {
2023-09-11 18:28:56 +03:00
if (!inputsValue.title || !inputsValue.description) {
return showNotification({
show: true,
text: "Заполните поля",
type: "error",
});
}
2023-05-16 00:24:52 +03:00
apiRequest("/task/update-task", {
method: "PUT",
data: {
task_id: taskInfo.id,
title: inputsValue.title,
2023-06-22 14:53:35 +03:00
description: inputsValue.description,
2023-05-16 00:24:52 +03:00
},
2023-08-30 17:02:32 +03:00
}).then(() => {
showNotification({
show: true,
text: "Изменения сохранены",
type: "success",
});
2023-09-11 18:28:56 +03:00
setEditOpen(!editOpen);
2023-08-30 17:02:32 +03:00
});
2023-05-16 00:24:52 +03:00
}
2023-05-23 23:02:39 +03:00
function createComment() {
2023-10-28 17:57:58 +03:00
if (!inputsValue.comment) return;
setCommentSendDisable(true);
2023-05-17 23:18:46 +03:00
apiRequest("/comment/create", {
method: "POST",
data: {
text: inputsValue.comment,
entity_type: 2,
2023-06-22 14:53:35 +03:00
entity_id: taskInfo.id,
},
2023-05-17 23:18:46 +03:00
}).then((res) => {
2023-06-22 14:53:35 +03:00
let newComment = res;
2023-10-28 17:57:58 +03:00
setCommentSendDisable(false);
2023-06-22 14:53:35 +03:00
newComment.created_at = new Date();
newComment.subComments = [];
setInputsValue((prevValue) => ({ ...prevValue, comment: "" }));
setComments((prevValue) => [...prevValue, newComment]);
});
2023-05-17 23:18:46 +03:00
}
2023-06-12 23:30:18 +03:00
function startTaskTimer() {
apiRequest("/timer/create", {
method: "POST",
2023-05-23 23:02:39 +03:00
data: {
2023-06-12 23:30:18 +03:00
entity_type: 2,
entity_id: taskInfo.id,
2023-06-22 14:53:35 +03:00
created_at: getCorrectRequestDate(new Date()),
},
2023-06-12 23:30:18 +03:00
}).then((res) => {
2023-06-22 14:53:35 +03:00
setTimerStart(true);
setTimerInfo(res);
2023-06-29 02:26:49 +03:00
startTimer();
2023-06-22 14:53:35 +03:00
});
2023-05-23 23:02:39 +03:00
}
2023-06-12 23:30:18 +03:00
function stopTaskTimer() {
apiRequest("/timer/update", {
2023-05-23 23:02:39 +03:00
method: "PUT",
data: {
2023-06-12 23:30:18 +03:00
timer_id: timerInfo.id,
2023-06-22 14:53:35 +03:00
stopped_at: getCorrectRequestDate(new Date()),
},
2023-06-29 02:26:49 +03:00
}).then(() => {
setTimerStart(false);
clearInterval(timerId);
});
2023-05-23 23:02:39 +03:00
}
function deletePerson(userId) {
apiRequest("/project/del-user", {
method: "DELETE",
data: {
2023-06-29 03:41:18 +03:00
project_id: projectInfo.id,
2023-06-22 14:53:35 +03:00
user_id: userId,
2023-05-23 23:02:39 +03:00
},
2023-05-31 11:24:46 +03:00
}).then(() => {
2023-06-22 14:53:35 +03:00
dispatch(deletePersonOnProject(userId));
2023-05-23 23:02:39 +03:00
});
}
2023-06-12 23:30:18 +03:00
function commentDelete(comment) {
2023-06-22 14:53:35 +03:00
setComments((prevValue) =>
prevValue.filter((item) => item.id !== comment.id)
);
2023-06-12 23:30:18 +03:00
if (comment.subComments.length) {
comment.subComments.forEach((subComment) => {
apiRequest("/comment/update", {
method: "PUT",
data: {
comment_id: subComment.id,
2023-06-22 14:53:35 +03:00
status: 0,
},
}).then(() => {});
});
2023-06-12 23:30:18 +03:00
}
}
function addSubComment(commentId, subComment) {
2023-06-22 14:53:35 +03:00
const addSubComment = comments;
2023-06-12 23:30:18 +03:00
addSubComment.forEach((comment) => {
if (comment.id === commentId) {
2023-06-22 14:53:35 +03:00
comment.subComments.push(subComment);
2023-06-12 23:30:18 +03:00
}
2023-06-22 14:53:35 +03:00
});
setComments(addSubComment);
2023-06-12 23:30:18 +03:00
}
function subCommentDelete(subComment) {
2023-06-22 14:53:35 +03:00
const deleteSubComment = comments;
2023-06-12 23:30:18 +03:00
deleteSubComment.forEach((comment, index) => {
if (comment.id === subComment.parent_id) {
2023-06-22 14:53:35 +03:00
deleteSubComment[index].subComments = comment.subComments.filter(
(item) => item.id !== subComment.id
);
2023-06-12 23:30:18 +03:00
}
2023-06-22 14:53:35 +03:00
});
setComments([...deleteSubComment]);
2023-06-12 23:30:18 +03:00
}
2023-04-20 20:10:08 +03:00
const toggleTabs = (index) => {
2023-05-02 18:51:19 +03:00
dispatch(setToggleTab(index));
2023-04-20 20:10:08 +03:00
};
function copyTicketLink() {
navigator.clipboard.writeText(
`https://itguild.info/tracker/task/${taskInfo.id}`
);
}
2023-06-29 02:26:49 +03:00
function startTimer() {
setTimerId(
setInterval(() => {
run();
}, 1000)
);
}
2023-06-29 03:41:18 +03:00
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]);
2023-06-29 02:26:49 +03:00
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;
}
2023-06-29 03:41:18 +03:00
function deleteTaskExecutor() {
apiRequest("/task/update-task", {
method: "PUT",
data: {
task_id: taskInfo.id,
executor_id: 0,
},
}).then(() => {
setTaskInfo((prevState) => ({
...prevState,
executor_id: null,
2023-06-29 03:41:59 +03:00
executor: null,
}));
2023-06-29 03:41:18 +03:00
});
}
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,
2023-06-29 03:41:59 +03:00
executor: res.executor,
}));
2023-06-29 03:41:18 +03:00
});
}
function deleteMember(person) {
apiRequest("/task/del-user", {
method: "DELETE",
data: {
task_id: taskInfo.id,
user_id: person.user_id,
},
}).then(() => {
setTaskInfo((prevState) => ({
...prevState,
2023-06-29 03:41:59 +03:00
taskUsers: taskInfo.taskUsers.filter(
(item) => item.user_id !== person.user_id
),
}));
2023-06-29 03:41:18 +03:00
});
}
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,
2023-06-29 03:41:59 +03:00
taskUsers: [...prevValue.taskUsers, res],
2023-06-29 03:41:18 +03:00
}));
});
}
2023-07-07 17:38:19 +03:00
function selectDeadLine(date) {
apiRequest("/task/update-task", {
method: "PUT",
data: {
task_id: taskInfo.id,
2023-07-07 17:38:31 +03:00
dead_line: getCorrectRequestDate(date),
2023-07-07 17:38:19 +03:00
},
2023-07-07 17:38:31 +03:00
}).then(() => {});
2023-07-07 17:38:19 +03:00
}
2023-07-13 15:49:07 +03:00
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,
2023-07-13 15:49:34 +03:00
status: 1,
},
2023-07-13 15:49:07 +03:00
}).then((res) => {
2023-07-13 15:49:34 +03:00
setTaskFiles((prevValue) => [...prevValue, res]);
setUploadedFile(null);
});
2023-07-13 15:49:07 +03:00
}
function deleteLoadedFile() {
2023-07-13 15:49:34 +03:00
setUploadedFile(null);
2023-07-13 15:49:07 +03:00
}
2023-11-21 16:40:58 +03:00
function updateTaskPriority(key) {
2023-11-21 16:41:18 +03:00
setSelectPriorityOpen(false);
2023-11-21 16:40:58 +03:00
apiRequest("/task/update-task", {
method: "PUT",
data: {
task_id: taskInfo.id,
2023-11-21 16:41:18 +03:00
execution_priority: key,
2023-11-21 16:40:58 +03:00
},
2023-11-21 16:41:18 +03:00
}).then(() => {});
2023-11-21 16:40:58 +03:00
}
2023-11-19 19:04:42 +03:00
// 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)
// );
// });
// }
2023-07-13 15:49:07 +03:00
function deleteFile(file) {
2023-11-19 19:04:42 +03:00
setTaskFiles((prevValue) =>
2023-11-19 20:01:27 +03:00
prevValue.filter((item) => item.id !== file.id)
2023-11-19 19:04:42 +03:00
);
2023-07-13 15:49:07 +03:00
}
2023-07-14 03:03:49 +03:00
function closeAcceptModal() {
setAcceptModalOpen(false);
2023-07-14 03:03:33 +03:00
}
2023-10-26 14:44:39 +03:00
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]);
});
}
2023-10-28 17:57:32 +03:00
const initListeners = () => {
document.addEventListener("click", closeByClickingOut);
};
const closeByClickingOut = (event) => {
const path = event.path || (event.composedPath && event.composedPath());
if (
2023-10-28 17:57:58 +03:00
event &&
!path.find(
(div) =>
div.classList &&
(div.classList.contains("button-add-worker") ||
div.classList.contains("dropdownList"))
)
2023-10-28 17:57:32 +03:00
) {
setDropListOpen(false);
2023-10-28 17:57:58 +03:00
setDropListMembersOpen(false);
2023-10-28 17:57:32 +03:00
}
if (
2023-10-28 17:57:58 +03:00
event &&
!path.find(
(div) =>
div.classList &&
(div.classList.contains("deadLine") ||
div.classList.contains("react-datepicker-popper"))
)
2023-10-28 17:57:32 +03:00
) {
2023-10-28 17:57:58 +03:00
setDatePickerOpen(false);
2023-10-28 17:57:32 +03:00
}
if (
2023-10-28 17:57:58 +03:00
event &&
!path.find(
(div) =>
div.classList &&
(div.classList.contains("tags") ||
div.classList.contains("tags__dropDown"))
)
2023-10-28 17:57:32 +03:00
) {
2023-10-28 17:57:58 +03:00
setSelectTagsOpen(false);
2023-10-28 17:57:32 +03:00
}
if (
2023-10-28 17:57:58 +03:00
event &&
!path.find(
(div) =>
div.classList &&
(div.classList.contains("addPerson") ||
div.classList.contains("persons__list"))
)
2023-10-28 17:57:32 +03:00
) {
2023-10-28 17:57:58 +03:00
setPersonListOpen(false);
2023-10-28 17:57:32 +03:00
}
2023-10-28 17:57:58 +03:00
};
2023-10-28 17:57:32 +03:00
2023-04-20 20:10:08 +03:00
return (
2023-06-22 14:53:35 +03:00
<section className="ticket-full-screen">
<ProfileHeader />
<Navigation />
<div className="container">
<div className="tracker__content">
<ProfileBreadcrumbs
links={[
{ name: "Главная", link: "/profile" },
{ name: "Трекер", link: "/profile/tracker" },
]}
/>
<h2 className="tracker__title">Управление проектами с трекером</h2>
2023-04-20 20:10:08 +03:00
</div>
2023-06-22 14:53:35 +03:00
</div>
<div className="tracker__tabs">
<div className="tracker__tabs__head">
<Link
to="/profile/tracker"
2023-08-07 15:52:58 +03:00
className="tab active-tab projectsTab"
2023-06-22 14:53:35 +03:00
onClick={() => toggleTabs(1)}
>
<img src={project} alt="img" />
<p>Проекты </p>
</Link>
<Link
to="/profile/tracker"
2023-08-07 15:52:58 +03:00
className="tab tasksTab"
2023-06-22 14:53:35 +03:00
onClick={() => toggleTabs(2)}
>
<img src={tasks} alt="img" />
<p>Все мои задачи</p>
</Link>
<Link
to="/profile/tracker"
2023-08-07 15:52:58 +03:00
className="tab archiveTab"
2023-06-22 14:53:35 +03:00
onClick={() => toggleTabs(3)}
>
<img src={archive} alt="img" />
<p>Архив</p>
</Link>
</div>
{loader ? (
<Loader />
) : (
<>
<div className="tracker__tabs__content content-tabs">
<div className="tasks__head">
2023-07-07 00:46:40 +03:00
<div className="tasks__head__wrapper tasks__head__wrapper__fullScreen">
2023-06-29 03:41:18 +03:00
<h5>Проект : {projectInfo.name}</h5>
2023-04-20 20:10:08 +03:00
2023-06-22 14:53:35 +03:00
<TrackerModal
active={modalAddWorker}
setActive={setModalAddWorker}
></TrackerModal>
2023-04-20 20:10:08 +03:00
2023-06-22 14:53:35 +03:00
<div className="tasks__head__persons">
2023-06-29 02:26:49 +03:00
<div className="projectPersons">
2023-06-29 03:41:18 +03:00
{projectInfo.projectUsers?.length &&
2023-06-29 03:41:59 +03:00
projectInfo.projectUsers.slice(0, 3).map((person) => {
return (
<img
key={person.user_id}
src={
person.user?.avatar
? urlForLocal(person.user.avatar)
: avatarMok
}
alt="avatar"
/>
);
})}
2023-06-29 02:26:49 +03:00
</div>
2023-10-10 14:22:43 +03:00
{projectInfo.projectUsers?.length > 3 && (
2023-10-10 16:31:40 +03:00
<span className="countPersons">+1</span>
2023-10-10 14:22:43 +03:00
)}
2023-06-22 14:53:35 +03:00
<span
className="addPerson"
onClick={() => {
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">
2023-06-29 03:41:18 +03:00
<span>{projectInfo.projectUsers?.length}</span>
2023-06-22 14:53:35 +03:00
участник
</div>
<div className="persons__list__info">
2023-07-04 16:19:58 +03:00
<span>В проекте - </span>
<p>{projectInfo.name}</p>
2023-06-22 14:53:35 +03:00
</div>
<div className="persons__list__items">
2023-06-29 03:41:18 +03:00
{projectInfo.projectUsers?.map((person) => {
2023-06-22 14:53:35 +03:00
return (
<div
className="persons__list__item"
key={person.user_id}
2023-05-24 19:23:24 +03:00
>
2023-06-22 14:53:35 +03:00
<img
className="avatar"
2023-06-29 03:41:18 +03:00
src={
person.user?.avatar
? urlForLocal(person.user.avatar)
: avatarMok
}
2023-06-22 14:53:35 +03:00
alt="avatar"
/>
<span>{person.user.fio}</span>
<img
className="delete"
src={close}
alt="delete"
onClick={() => deletePerson(person.user_id)}
/>
2023-05-24 19:23:24 +03:00
</div>
2023-06-22 14:53:35 +03:00
);
})}
2023-05-24 19:23:24 +03:00
</div>
2023-06-22 14:53:35 +03:00
<div
className="persons__list__add"
onClick={() => {
dispatch(modalToggle("addWorker"));
setModalAddWorker(true);
setPersonListOpen(false);
}}
>
<span className="addPerson">+</span>
<p>Добавить участников</p>
</div>
</div>
)}
</div>
2023-11-08 18:27:58 +03:00
<Link
to={`/tracker/project/${taskInfo.project_id}`}
className="link"
>
2023-06-22 14:53:35 +03:00
<div className="tasks__head__back">
2023-11-08 18:27:42 +03:00
<p>Вернуться на проект</p>
2023-06-22 14:53:35 +03:00
<img src={arrow} alt="arrow" />
2023-06-12 23:30:18 +03:00
</div>
2023-06-22 14:53:35 +03:00
</Link>
</div>
</div>
</div>
<div className="modal-tiket__content ticket">
<div className="content ticket-whith">
<div className="content__task">
{editOpen ? (
<input
2023-07-07 00:46:40 +03:00
maxLength="100"
2023-06-22 14:53:35 +03:00
value={inputsValue.title}
onChange={(e) => {
setInputsValue((prevValue) => ({
...prevValue,
title: e.target.value,
}));
}}
/>
) : (
2023-08-07 15:53:18 +03:00
<h5 className="fullName nameFullScreen">
{inputsValue.title}
</h5>
2023-06-22 14:53:35 +03:00
)}
<div className="content__description">
{editOpen ? (
2023-06-29 02:26:49 +03:00
<CKEditor
editor={ClassicEditor}
data={inputsValue.description}
config={{
removePlugins: [
"CKFinderUploadAdapter",
"CKFinder",
"EasyImage",
"Image",
"ImageCaption",
"ImageStyle",
"ImageToolbar",
"ImageUpload",
"MediaEmbed",
"BlockQuote",
],
}}
onChange={(event, editor) => {
const data = editor.getData();
2023-06-22 14:53:35 +03:00
setInputsValue((prevValue) => ({
...prevValue,
2023-06-29 02:26:49 +03:00
description: data,
2023-06-22 14:53:35 +03:00
}));
}}
/>
) : (
2023-06-29 02:27:08 +03:00
<p
2023-08-07 15:52:58 +03:00
className="fullDescription fullScreenDescription"
2023-06-29 02:27:08 +03:00
dangerouslySetInnerHTML={{
__html: inputsValue.description,
}}
/>
2023-06-22 14:53:35 +03:00
)}
</div>
2023-07-13 15:49:34 +03:00
{Boolean(taskFiles.length) && (
2023-08-07 15:52:58 +03:00
<div className="task__files filesFullScreen">
2023-07-13 15:49:34 +03:00
{taskFiles.map((file) => {
return (
2023-11-19 20:01:27 +03:00
<FileTracker
key={file.id}
file={file}
setDeletedTask={deleteFile}
taskId={taskInfo.id}
/>
2023-07-13 15:49:34 +03:00
);
})}
</div>
)}
2023-07-13 15:49:07 +03:00
{uploadedFile && (
2023-07-13 15:49:34 +03:00
<div className="fileLoaded">
{uploadedFile.map((file) => {
return (
<div className="loadedFile" key={file.id}>
<img
src={backendImg(file.url)}
alt="img"
key={file.id}
/>
<div
className="deleteFile"
onClick={() => deleteLoadedFile(file)}
>
<img src={fileDelete} alt="delete" />
</div>
</div>
);
})}
<button onClick={attachFile}>Загрузить</button>
</div>
2023-07-13 15:49:07 +03:00
)}
2023-06-22 14:53:35 +03:00
<div className="content__communication">
2023-06-29 03:41:18 +03:00
{/*<p className="tasks">*/}
{/* <BaseButton*/}
{/* onClick={() => {*/}
{/* dispatch(modalToggle("addSubtask"));*/}
{/* setModalAddWorker(true);*/}
{/* }}*/}
{/* styles={"button-green-add"}*/}
{/* >*/}
{/* <img src={plus}></img>*/}
{/* Добавить под задачу*/}
{/* </BaseButton>*/}
{/*</p>*/}
2023-07-13 15:49:07 +03:00
<div className="file">
<div className="input__wrapper">
<input
2023-07-13 15:49:34 +03:00
name="file"
id="input__file"
type="file"
accept="image/*,.png,.jpg,.svg,.jpeg"
className="input__file"
onChange={handleUpload}
2023-07-13 15:49:07 +03:00
/>
2023-07-13 15:49:34 +03:00
<label
htmlFor="input__file"
className="button-add-file"
>
2023-07-13 15:49:07 +03:00
<img src={file}></img>
Загрузить файл
</label>
</div>
<span>{taskFiles.length ? taskFiles.length : 0}</span>
{caseOfNum(taskFiles.length, "files")}
</div>
2023-06-22 14:53:35 +03:00
</div>
2023-08-07 15:52:58 +03:00
<div className="content__input commentFullScreen">
2023-06-22 14:53:35 +03:00
<input
placeholder="Оставить комментарий"
value={inputsValue.comment}
onChange={(e) => {
setInputsValue((prevValue) => ({
...prevValue,
comment: e.target.value,
}));
}}
/>
2023-10-28 17:57:58 +03:00
<img
className={commentSendDisable ? "disable" : ""}
src={send}
onClick={createComment}
alt="send"
></img>
2023-06-22 14:53:35 +03:00
</div>
<div className="comments__list">
{comments.map((comment) => {
return (
<TrackerTaskComment
key={comment.id}
taskId={taskInfo.id}
comment={comment}
commentDelete={commentDelete}
addSubComment={addSubComment}
subCommentDelete={subCommentDelete}
/>
);
})}
2023-05-24 19:23:24 +03:00
</div>
</div>
2023-06-22 14:53:35 +03:00
</div>
2023-08-07 15:52:58 +03:00
<div className="workers fullScreenWorkers">
2023-06-29 02:26:49 +03:00
<div className="workers_box task__info">
2023-06-22 14:53:35 +03:00
<p className="workers__creator">
2023-08-07 15:53:18 +03:00
Создатель :<p>&nbsp;{taskInfo.user?.fio}</p>
2023-06-22 14:53:35 +03:00
</p>
2023-04-20 20:10:08 +03:00
2023-06-29 03:41:18 +03:00
{taskInfo.executor ? (
<div className="executor">
<p>Исполнитель: {taskInfo.executor.fio}</p>
<img
src={
taskInfo.executor?.avatar
? urlForLocal(taskInfo.executor.avatar)
: avatarMok
}
2023-06-29 03:41:59 +03:00
alt="avatar"
/>
2023-06-29 03:41:18 +03:00
<img
src={close}
className="delete"
onClick={() => deleteTaskExecutor()}
/>
</div>
) : (
<div className="add-worker moreItems ">
<button
className="button-add-worker"
onClick={() => setDropListOpen(true)}
>
+
</button>
<span>Добавить исполнителя</span>
{dropListOpen && (
<div className="dropdownList">
<img
src={close}
className="dropdownList__close"
onClick={() => setDropListOpen(false)}
/>
{correctProjectUsers.map((person) => {
return (
<div
className="dropdownList__person"
key={person.user_id}
onClick={() => taskExecutor(person)}
>
<span>{person.user.fio}</span>
<img
src={
person.user?.avatar
? urlForLocal(person.user.avatar)
: avatarMok
}
2023-06-29 03:41:59 +03:00
alt="avatar"
2023-06-29 03:41:18 +03:00
/>
</div>
);
})}
</div>
)}
</div>
)}
{Boolean(taskInfo.taskUsers.length) && (
<div className="members">
<p>Участники:</p>
<div className="members__list">
{taskInfo.taskUsers.map((member) => {
return (
<div className="worker" key={member.user_id}>
<p>{member.fio}</p>
<img
src={
member?.avatar
? urlForLocal(member.avatar)
: avatarMok
}
2023-06-29 03:41:59 +03:00
alt="avatar"
2023-06-29 03:41:18 +03:00
/>
<img
src={close}
className="delete"
onClick={() => deleteMember(member)}
/>
</div>
);
})}
</div>
</div>
)}
2023-06-22 14:53:35 +03:00
<div className="add-worker moreItems">
2023-06-29 03:41:18 +03:00
<button
className="button-add-worker"
onClick={() => setDropListMembersOpen(true)}
2023-06-22 14:53:35 +03:00
>
+
2023-06-29 03:41:18 +03:00
</button>
2023-06-22 14:53:35 +03:00
<span>Добавить участников</span>
2023-06-29 03:41:18 +03:00
{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={
person.user?.avatar
? urlForLocal(person.user.avatar)
: avatarMok
}
2023-06-29 03:41:59 +03:00
alt="avatar"
2023-06-29 03:41:18 +03:00
/>
</div>
);
})
) : (
<p className="noUsers">Нет пользователей</p>
)}
</div>
)}
2023-06-22 14:53:35 +03:00
</div>
</div>
2023-04-20 20:10:08 +03:00
2023-06-22 14:53:35 +03:00
<div className="workers_box-middle">
2023-07-07 17:38:31 +03:00
<div className="deadLine">
<div
className="deadLine__container"
onClick={() => setDatePickerOpen(!datePickerOpen)}
>
<img src={calendarIcon} alt="calendar" />
<span>
{deadLine
? getCorrectDate(deadLine)
: "Срок исполнения:"}
</span>
2023-07-07 17:38:19 +03:00
</div>
<DatePicker
2023-07-07 17:38:31 +03:00
className="datePicker"
open={datePickerOpen}
locale="ru"
selected={startDate}
onChange={(date) => {
setDatePickerOpen(false);
setStartDate(date);
setDeadLine(date);
selectDeadLine(date);
}}
2023-07-07 17:38:19 +03:00
/>
</div>
2023-06-22 14:53:35 +03:00
<div className="time">
2023-06-29 02:26:49 +03:00
<img src={watch}></img>
2023-06-22 14:53:35 +03:00
<span>Длительность : </span>
2023-06-29 02:26:49 +03:00
<p>
{correctTimerTime(currentTimerCount.hours)}:
{correctTimerTime(currentTimerCount.minute)}:
{correctTimerTime(currentTimerCount.seconds)}
</p>
2023-06-22 14:53:35 +03:00
</div>
2023-04-20 20:10:08 +03:00
2023-06-22 14:53:35 +03:00
{timerStart ? (
2023-06-29 02:26:49 +03:00
<button
className={
2023-06-29 02:27:08 +03:00
taskInfo.executor_id ===
Number(localStorage.getItem("id"))
2023-06-29 02:26:49 +03:00
? "stop"
: "stop disable"
}
onClick={() => stopTaskTimer()}
>
2023-06-22 14:53:35 +03:00
Остановить
</button>
) : (
<button
className={
2023-06-29 02:27:08 +03:00
taskInfo.executor_id ===
Number(localStorage.getItem("id"))
2023-06-22 14:53:35 +03:00
? "start"
: "start disable"
2023-05-24 19:23:24 +03:00
}
2023-06-22 14:53:35 +03:00
onClick={() => startTaskTimer()}
>
2023-06-29 02:26:49 +03:00
Начать делать
<img src={arrowStart}></img>
2023-06-22 14:53:35 +03:00
</button>
)}
</div>
2023-11-21 16:40:58 +03:00
<div className="workers_box-tag">
2023-10-26 14:44:39 +03:00
<div className="tags">
<div className="tags__selected">
{taskTags.map((tag) => {
return (
2023-11-21 16:41:18 +03:00
<div
className="tags__selected__item"
key={tag.id}
style={{ background: tag.color }}
>
<p>{tag.slug}</p>
<img
src={close}
className="delete"
alt="delete"
onClick={() => deleteTagFromTask(tag.id)}
/>
</div>
2023-10-26 14:44:39 +03:00
);
})}
</div>
<div
2023-11-21 16:41:18 +03:00
className="tags__select"
onClick={() => setSelectTagsOpen(!selectTagsOpen)}
2023-10-26 14:44:39 +03:00
>
2023-11-21 16:53:05 +03:00
<span>Выберите тег</span>
2023-10-26 14:44:39 +03:00
<img
2023-11-21 16:41:18 +03:00
className={selectTagsOpen ? "open" : ""}
src={arrowDown}
alt="arrow"
2023-10-26 14:44:39 +03:00
/>
</div>
{selectTagsOpen && (
2023-11-21 16:41:18 +03:00
<div className="tags__dropDown">
<img
onClick={() => setSelectTagsOpen(false)}
className="tags__dropDown__close"
src={close}
alt="close"
/>
{correctProjectTags.map((tag) => {
return (
<div
className="tagItem"
key={tag.id}
onClick={() => addTagToTask(tag.id)}
>
<p>{tag.slug}</p>
<span style={{ background: tag.color }} />
</div>
);
})}
{!Boolean(correctProjectTags.length) && (
<p className="tags__dropDown__noItem">Нет тегов</p>
)}
</div>
2023-10-26 14:44:39 +03:00
)}
</div>
2023-11-21 16:40:58 +03:00
</div>
2023-11-21 16:41:18 +03:00
<div className="workers_box-priority">
<div
className="priority__name"
onClick={() => setSelectPriorityOpen(!selectPriorityOpen)}
>
<span>
{typeof taskPriority === "number"
2023-12-04 17:30:52 +03:00
? `Приоритет: ${priority[taskPriority]}`
2023-11-21 16:53:05 +03:00
: "Выберите приоритет"}
2023-11-21 16:41:18 +03:00
</span>
2023-11-21 16:40:58 +03:00
<img
2023-11-21 16:41:18 +03:00
className={selectPriorityOpen ? "open" : ""}
src={arrowDown}
alt="arrow"
2023-11-21 16:40:58 +03:00
/>
</div>
2023-11-21 16:41:18 +03:00
{selectPriorityOpen && (
<div className="priority__dropDown">
{priorityTypes.map((item) => {
return (
<div
className="priority__dropDown__item"
key={item.key}
onClick={() => {
setTaskPriority(item.key);
updateTaskPriority(item.key);
}}
>
{item.name}
</div>
);
})}
</div>
)}
2023-11-21 16:40:58 +03:00
</div>
<div className="workers_box-bottom">
2023-06-22 14:53:35 +03:00
<div
className={editOpen ? "edit" : ""}
onClick={() => {
if (editOpen) {
editTask();
} else {
setEditOpen(!editOpen);
}
}}
>
<img src={edit} alt="edit"></img>
<p>{editOpen ? "сохранить" : "редактировать"}</p>
</div>
<div>
<img src={link} alt="link"></img>
<p onClick={copyTicketLink}>ссылка на задачу</p>
2023-06-22 14:53:35 +03:00
</div>
2023-08-31 01:58:48 +03:00
<div
onClick={archiveTask}
className={
profileInfo.id_user === projectInfo.owner_id ||
profileInfo.id_user === taskInfo.user_id
? ""
: "disable"
}
>
2023-07-07 00:46:40 +03:00
<img src={archive} alt="arch"></img>
2023-06-22 14:53:35 +03:00
<p>в архив</p>
</div>
2023-08-31 01:58:48 +03:00
<div
onClick={deleteTask}
className={
profileInfo.id_user === projectInfo.owner_id ||
profileInfo.id_user === taskInfo.user_id
? ""
: "disable"
}
2023-08-31 01:58:35 +03:00
>
2023-06-22 14:53:35 +03:00
<img src={del} alt="delete"></img>
<p>удалить</p>
2023-05-24 19:23:24 +03:00
</div>
</div>
2023-06-22 14:53:35 +03:00
</div>
</div>
</>
)}
</div>
2023-07-14 03:03:49 +03:00
{acceptModalOpen && (
2023-11-03 16:23:21 +03:00
<AcceptModal
2023-11-03 16:23:49 +03:00
title={"Вы точно хотите переместить задачу в архив?"}
closeModal={closeAcceptModal}
agreeHandler={deleteTask}
/>
2023-07-14 03:03:49 +03:00
)}
2023-06-22 14:53:35 +03:00
<Footer />
</section>
2023-04-20 20:10:08 +03:00
);
};
export default TicketFullScreen;