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";
|
2024-01-30 20:16:21 +03:00
|
|
|
|
import { getBoarderLoader, setToggleTab } from "@redux/projectsTrackerSlice";
|
2023-05-31 08:36:15 +03:00
|
|
|
|
|
2024-02-15 15:37:15 +03:00
|
|
|
|
import { getCorrectDate } from "@utils/calendarHelper";
|
2023-07-13 15:49:34 +03:00
|
|
|
|
import {
|
|
|
|
|
backendImg,
|
|
|
|
|
caseOfNum,
|
|
|
|
|
getCorrectRequestDate,
|
|
|
|
|
getToken,
|
2024-03-06 12:29:13 +03:00
|
|
|
|
removeLast,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
urlForLocal
|
2023-07-13 15:49:34 +03:00
|
|
|
|
} 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-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 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-07-13 15:49:07 +03:00
|
|
|
|
import fileDelete from "assets/icons/closeProjectPersons.svg";
|
2024-02-20 15:17:05 +03:00
|
|
|
|
import close from "assets/icons/crossWhite.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-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 [timerStart, setTimerStart] = useState(false);
|
|
|
|
|
const [timerInfo, setTimerInfo] = useState({});
|
2023-06-29 02:26:49 +03:00
|
|
|
|
const [currentTimerCount, setCurrentTimerCount] = useState({
|
|
|
|
|
hours: 0,
|
|
|
|
|
minute: 0,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
seconds: 0
|
2023-06-29 02:26:49 +03:00
|
|
|
|
});
|
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: "Средний",
|
2023-12-19 17:36:30 +03:00
|
|
|
|
0: "Низкий"
|
2023-11-21 16:41:18 +03:00
|
|
|
|
};
|
2023-11-21 16:40:58 +03:00
|
|
|
|
|
|
|
|
|
const priorityTypes = [
|
|
|
|
|
{
|
2023-11-21 16:41:18 +03:00
|
|
|
|
name: "Высокий",
|
2023-12-19 17:36:30 +03:00
|
|
|
|
key: 2
|
2023-11-21 16:40:58 +03:00
|
|
|
|
},
|
|
|
|
|
{
|
2023-11-21 16:41:18 +03:00
|
|
|
|
name: "Средний",
|
2023-12-19 17:36:30 +03:00
|
|
|
|
key: 1
|
2023-11-21 16:40:58 +03:00
|
|
|
|
},
|
|
|
|
|
{
|
2023-11-21 16:41:18 +03:00
|
|
|
|
name: "Низкий",
|
2023-12-19 17:36:30 +03:00
|
|
|
|
key: 0
|
|
|
|
|
}
|
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,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
comment: ""
|
2023-10-13 14:39:01 +03:00
|
|
|
|
});
|
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),
|
2023-12-19 17:36:30 +03:00
|
|
|
|
seconds: timerSeconds % 60
|
2023-10-13 14:39:01 +03:00
|
|
|
|
});
|
|
|
|
|
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,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
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: "Заполните поля",
|
2023-12-19 17:36:30 +03:00
|
|
|
|
type: "error"
|
2023-09-11 18:28:56 +03:00
|
|
|
|
});
|
|
|
|
|
}
|
2023-05-16 00:24:52 +03:00
|
|
|
|
apiRequest("/task/update-task", {
|
|
|
|
|
method: "PUT",
|
|
|
|
|
data: {
|
|
|
|
|
task_id: taskInfo.id,
|
|
|
|
|
title: inputsValue.title,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
description: inputsValue.description
|
|
|
|
|
}
|
2023-08-30 17:02:32 +03:00
|
|
|
|
}).then(() => {
|
|
|
|
|
showNotification({
|
|
|
|
|
show: true,
|
|
|
|
|
text: "Изменения сохранены",
|
2023-12-19 17:36:30 +03:00
|
|
|
|
type: "success"
|
2023-08-30 17:02:32 +03:00
|
|
|
|
});
|
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-12-19 17:36:30 +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-12-19 17:36:30 +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-12-19 17:36:30 +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
|
|
|
|
}
|
|
|
|
|
|
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-12-19 17:36:30 +03:00
|
|
|
|
status: 0
|
|
|
|
|
}
|
2023-06-22 14:53:35 +03:00
|
|
|
|
}).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
|
|
|
|
};
|
|
|
|
|
|
2023-06-27 17:14:22 +03:00
|
|
|
|
function copyTicketLink() {
|
|
|
|
|
navigator.clipboard.writeText(
|
|
|
|
|
`https://itguild.info/tracker/task/${taskInfo.id}`
|
|
|
|
|
);
|
2023-06-27 17:12:58 +03:00
|
|
|
|
}
|
|
|
|
|
|
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,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
seconds: updateTimerSec
|
2023-06-29 02:26:49 +03:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
executor_id: 0
|
|
|
|
|
}
|
2023-06-29 03:41:18 +03:00
|
|
|
|
}).then(() => {
|
|
|
|
|
setTaskInfo((prevState) => ({
|
|
|
|
|
...prevState,
|
|
|
|
|
executor_id: null,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
executor: null
|
2023-06-29 03:41:59 +03:00
|
|
|
|
}));
|
2023-06-29 03:41:18 +03:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function taskExecutor(person) {
|
|
|
|
|
apiRequest("/task/update-task", {
|
|
|
|
|
method: "PUT",
|
|
|
|
|
data: {
|
|
|
|
|
task_id: taskInfo.id,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
executor_id: person.user_id
|
|
|
|
|
}
|
2023-06-29 03:41:18 +03:00
|
|
|
|
}).then((res) => {
|
|
|
|
|
setDropListOpen(false);
|
|
|
|
|
setTaskInfo((prevState) => ({
|
|
|
|
|
...prevState,
|
|
|
|
|
executor_id: res.executor_id,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
executor: res.executor
|
2023-06-29 03:41:59 +03:00
|
|
|
|
}));
|
2023-06-29 03:41:18 +03:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function deleteMember(person) {
|
|
|
|
|
apiRequest("/task/del-user", {
|
|
|
|
|
method: "DELETE",
|
|
|
|
|
data: {
|
|
|
|
|
task_id: taskInfo.id,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
user_id: person.user_id
|
|
|
|
|
}
|
2023-06-29 03:41:18 +03:00
|
|
|
|
}).then(() => {
|
|
|
|
|
setTaskInfo((prevState) => ({
|
|
|
|
|
...prevState,
|
2023-06-29 03:41:59 +03:00
|
|
|
|
taskUsers: taskInfo.taskUsers.filter(
|
|
|
|
|
(item) => item.user_id !== person.user_id
|
2023-12-19 17:36:30 +03:00
|
|
|
|
)
|
2023-06-29 03:41:59 +03:00
|
|
|
|
}));
|
2023-06-29 03:41:18 +03:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function addMember(person) {
|
|
|
|
|
apiRequest("/task/add-user-to-task", {
|
|
|
|
|
method: "POST",
|
|
|
|
|
data: {
|
|
|
|
|
task_id: taskInfo.id,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
user_id: person.user_id
|
|
|
|
|
}
|
2023-06-29 03:41:18 +03:00
|
|
|
|
}).then((res) => {
|
|
|
|
|
setDropListMembersOpen(false);
|
|
|
|
|
setTaskInfo((prevValue) => ({
|
|
|
|
|
...prevValue,
|
2023-12-19 17:36:30 +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-12-19 17:36:30 +03:00
|
|
|
|
dead_line: getCorrectRequestDate(date)
|
|
|
|
|
}
|
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,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
headers: { ...getToken() }
|
2023-07-13 15:49:07 +03:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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-12-19 17:36:30 +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-12-19 17:36:30 +03:00
|
|
|
|
execution_priority: key
|
|
|
|
|
}
|
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,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
entity_id: taskInfo.id
|
|
|
|
|
}
|
2023-10-26 14:44:39 +03:00
|
|
|
|
}).then(() => {
|
|
|
|
|
setTaskTags((prevValue) => prevValue.filter((tag) => tag.id !== tagId));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function addTagToTask(tagId) {
|
|
|
|
|
apiRequest("/mark/attach", {
|
|
|
|
|
method: "POST",
|
|
|
|
|
data: {
|
|
|
|
|
mark_id: tagId,
|
|
|
|
|
entity_type: 2,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
entity_id: taskInfo.id
|
|
|
|
|
}
|
2023-10-26 14:44:39 +03:00
|
|
|
|
}).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") ||
|
2024-02-09 18:17:15 +03:00
|
|
|
|
div.classList.contains("dropdown-list"))
|
2023-10-28 17:57:58 +03:00
|
|
|
|
)
|
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 &&
|
2024-02-09 18:17:15 +03:00
|
|
|
|
(div.classList.contains("dead-line") ||
|
2023-10-28 17:57:58 +03:00
|
|
|
|
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
|
|
|
|
}
|
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" },
|
2023-12-19 17:36:30 +03:00
|
|
|
|
{ name: "Трекер", link: "/profile/tracker" }
|
2023-06-22 14:53:35 +03:00
|
|
|
|
]}
|
|
|
|
|
/>
|
|
|
|
|
<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"
|
2024-02-06 19:57:27 +03:00
|
|
|
|
className="tab active-tab"
|
2023-06-22 14:53:35 +03:00
|
|
|
|
onClick={() => toggleTabs(1)}
|
|
|
|
|
>
|
|
|
|
|
<img src={project} alt="img" />
|
|
|
|
|
<p>Проекты </p>
|
|
|
|
|
</Link>
|
|
|
|
|
<Link
|
|
|
|
|
to="/profile/tracker"
|
2024-02-06 19:57:27 +03:00
|
|
|
|
className="tab"
|
2023-06-22 14:53:35 +03:00
|
|
|
|
onClick={() => toggleTabs(2)}
|
|
|
|
|
>
|
|
|
|
|
<img src={tasks} 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">
|
2024-01-30 17:40:40 +03:00
|
|
|
|
<h5>{projectInfo.name}</h5>
|
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">
|
2024-01-30 17:40:40 +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>
|
2024-01-31 16:57:22 +03:00
|
|
|
|
<div className="modal-ticket__content ticket">
|
2023-06-22 14:53:35 +03:00
|
|
|
|
<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,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
title: e.target.value
|
2023-06-22 14:53:35 +03:00
|
|
|
|
}));
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
2024-02-09 18:17:15 +03:00
|
|
|
|
<h5 className="full-name name-fullscreen">
|
2024-01-26 18:07:32 +03:00
|
|
|
|
{inputsValue.title}
|
2023-08-07 15:53:18 +03:00
|
|
|
|
</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",
|
2023-12-19 17:36:30 +03:00
|
|
|
|
"BlockQuote"
|
|
|
|
|
]
|
2023-06-29 02:26:49 +03:00
|
|
|
|
}}
|
|
|
|
|
onChange={(event, editor) => {
|
|
|
|
|
const data = editor.getData();
|
2023-06-22 14:53:35 +03:00
|
|
|
|
setInputsValue((prevValue) => ({
|
|
|
|
|
...prevValue,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
description: data
|
2023-06-22 14:53:35 +03:00
|
|
|
|
}));
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
2024-01-26 18:07:10 +03:00
|
|
|
|
<div className="ticket__description">
|
2024-01-30 20:16:21 +03:00
|
|
|
|
<div
|
2024-02-09 18:17:15 +03:00
|
|
|
|
className="full-description fullscreen-description"
|
2024-01-26 18:07:10 +03:00
|
|
|
|
dangerouslySetInnerHTML={{
|
|
|
|
|
__html: inputsValue.description
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2023-06-22 14:53:35 +03:00
|
|
|
|
)}
|
|
|
|
|
</div>
|
2023-07-13 15:49:34 +03:00
|
|
|
|
{Boolean(taskFiles.length) && (
|
2024-02-09 18:17:15 +03:00
|
|
|
|
<div className="task__files files-fullscreen">
|
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 && (
|
2024-02-09 18:17:15 +03:00
|
|
|
|
<div className="file-loaded">
|
2023-07-13 15:49:34 +03:00
|
|
|
|
{uploadedFile.map((file) => {
|
|
|
|
|
return (
|
2024-02-09 18:17:15 +03:00
|
|
|
|
<div className="loaded-file" key={file.id}>
|
2023-07-13 15:49:34 +03:00
|
|
|
|
<img
|
|
|
|
|
src={backendImg(file.url)}
|
|
|
|
|
alt="img"
|
|
|
|
|
key={file.id}
|
|
|
|
|
/>
|
|
|
|
|
<div
|
2024-02-09 18:17:15 +03:00
|
|
|
|
className="delete-file"
|
2023-07-13 15:49:34 +03:00
|
|
|
|
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-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>
|
2024-02-09 18:17:15 +03:00
|
|
|
|
<div className="content__input comment-fullscreen">
|
2023-06-22 14:53:35 +03:00
|
|
|
|
<input
|
|
|
|
|
placeholder="Оставить комментарий"
|
|
|
|
|
value={inputsValue.comment}
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
setInputsValue((prevValue) => ({
|
|
|
|
|
...prevValue,
|
2023-12-19 17:36:30 +03:00
|
|
|
|
comment: e.target.value
|
2023-06-22 14:53:35 +03:00
|
|
|
|
}));
|
|
|
|
|
}}
|
|
|
|
|
/>
|
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>
|
2024-02-09 18:17:15 +03:00
|
|
|
|
<div className="workers fullscreen-workers">
|
2023-06-29 02:26:49 +03:00
|
|
|
|
<div className="workers_box task__info">
|
2024-01-30 20:16:21 +03:00
|
|
|
|
<div className="workers__creator">
|
2024-03-06 12:29:13 +03:00
|
|
|
|
Создатель: <p>{removeLast(taskInfo.user?.fio)}</p>
|
2024-01-30 20:16:21 +03:00
|
|
|
|
</div>
|
2023-04-20 20:10:08 +03:00
|
|
|
|
|
2023-06-29 03:41:18 +03:00
|
|
|
|
{taskInfo.executor ? (
|
2024-01-30 20:16:21 +03:00
|
|
|
|
<>
|
|
|
|
|
<h5>Исполнитель: </h5>
|
|
|
|
|
<div className="executor">
|
2024-03-06 12:29:13 +03:00
|
|
|
|
<p>{removeLast(taskInfo.executor.fio)}</p>
|
2024-01-30 20:16:21 +03:00
|
|
|
|
<img
|
|
|
|
|
src={
|
|
|
|
|
taskInfo.executor?.avatar
|
|
|
|
|
? urlForLocal(taskInfo.executor.avatar)
|
|
|
|
|
: avatarMok
|
|
|
|
|
}
|
|
|
|
|
alt="avatar"
|
|
|
|
|
/>
|
|
|
|
|
<img
|
|
|
|
|
src={close}
|
|
|
|
|
className="delete"
|
|
|
|
|
onClick={() => deleteTaskExecutor()}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
2023-06-29 03:41:18 +03:00
|
|
|
|
) : (
|
2024-02-09 18:17:15 +03:00
|
|
|
|
<div className="add-worker more-items ">
|
2023-06-29 03:41:18 +03:00
|
|
|
|
<button
|
|
|
|
|
className="button-add-worker"
|
|
|
|
|
onClick={() => setDropListOpen(true)}
|
|
|
|
|
>
|
|
|
|
|
+
|
|
|
|
|
</button>
|
|
|
|
|
<span>Добавить исполнителя</span>
|
|
|
|
|
{dropListOpen && (
|
2024-02-09 18:17:15 +03:00
|
|
|
|
<div className="dropdown-list">
|
2023-06-29 03:41:18 +03:00
|
|
|
|
<img
|
|
|
|
|
src={close}
|
2024-02-09 18:17:15 +03:00
|
|
|
|
className="dropdown-list__close"
|
2023-06-29 03:41:18 +03:00
|
|
|
|
onClick={() => setDropListOpen(false)}
|
|
|
|
|
/>
|
|
|
|
|
{correctProjectUsers.map((person) => {
|
|
|
|
|
return (
|
|
|
|
|
<div
|
2024-02-09 18:17:15 +03:00
|
|
|
|
className="dropdown-list__person"
|
2023-06-29 03:41:18 +03:00
|
|
|
|
key={person.user_id}
|
|
|
|
|
onClick={() => taskExecutor(person)}
|
|
|
|
|
>
|
2024-03-06 12:29:13 +03:00
|
|
|
|
<span>{removeLast(person.user.fio)}</span>
|
2023-06-29 03:41:18 +03:00
|
|
|
|
<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}>
|
2024-03-06 12:29:13 +03:00
|
|
|
|
<p>{removeLast(member.fio)}</p>
|
2023-06-29 03:41:18 +03:00
|
|
|
|
<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>
|
|
|
|
|
)}
|
2024-02-09 18:17:15 +03:00
|
|
|
|
<div className="add-worker more-items">
|
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>
|
2024-02-08 19:53:39 +03:00
|
|
|
|
<span>Добавить участника</span>
|
2023-06-29 03:41:18 +03:00
|
|
|
|
{dropListMembersOpen && (
|
2024-02-09 18:17:15 +03:00
|
|
|
|
<div className="dropdown-list">
|
2023-06-29 03:41:18 +03:00
|
|
|
|
<img
|
|
|
|
|
src={close}
|
2024-02-09 18:17:15 +03:00
|
|
|
|
className="dropdown-list__close"
|
2023-06-29 03:41:18 +03:00
|
|
|
|
onClick={() => setDropListMembersOpen(false)}
|
|
|
|
|
/>
|
|
|
|
|
{users.length ? (
|
|
|
|
|
users.map((person) => {
|
|
|
|
|
return (
|
|
|
|
|
<div
|
2024-02-09 18:17:15 +03:00
|
|
|
|
className="dropdown-list__person"
|
2023-06-29 03:41:18 +03:00
|
|
|
|
key={person.user_id}
|
|
|
|
|
onClick={() => addMember(person)}
|
|
|
|
|
>
|
2024-03-06 12:29:13 +03:00
|
|
|
|
<span>{removeLast(person.user.fio)}</span>
|
2023-06-29 03:41:18 +03:00
|
|
|
|
<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>
|
|
|
|
|
);
|
|
|
|
|
})
|
|
|
|
|
) : (
|
2024-02-09 18:17:15 +03:00
|
|
|
|
<p className="no-users">Нет пользователей</p>
|
2023-06-29 03:41:18 +03:00
|
|
|
|
)}
|
|
|
|
|
</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">
|
2024-02-09 18:17:15 +03:00
|
|
|
|
<div className="dead-line">
|
2023-07-07 17:38:31 +03:00
|
|
|
|
<div
|
2024-02-09 18:17:15 +03:00
|
|
|
|
className="dead-line__container"
|
2023-07-07 17:38:31 +03:00
|
|
|
|
onClick={() => setDatePickerOpen(!datePickerOpen)}
|
|
|
|
|
>
|
2024-02-28 19:44:12 +03:00
|
|
|
|
<p>⌛</p>
|
2023-07-07 17:38:31 +03:00
|
|
|
|
<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">
|
2024-02-15 17:01:50 +03:00
|
|
|
|
<p>⏱</p>
|
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
|
2024-02-09 18:17:15 +03:00
|
|
|
|
className="tag-item"
|
2023-11-21 16:41:18 +03:00
|
|
|
|
key={tag.id}
|
|
|
|
|
onClick={() => addTagToTask(tag.id)}
|
|
|
|
|
>
|
|
|
|
|
<p>{tag.slug}</p>
|
|
|
|
|
<span style={{ background: tag.color }} />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
{!Boolean(correctProjectTags.length) && (
|
2024-02-09 18:17:15 +03:00
|
|
|
|
<p className="tags__dropDown__no-item">Нет тегов</p>
|
2023-11-21 16:41:18 +03:00
|
|
|
|
)}
|
|
|
|
|
</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>
|
2023-06-27 17:12:58 +03:00
|
|
|
|
<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
|
2024-02-09 16:15:37 +03:00
|
|
|
|
title={"Вы действительно хотите переместить задачу в архив?"}
|
2023-11-03 16:23:49 +03:00
|
|
|
|
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;
|