diff --git a/src/assets/icons/calendar.svg b/src/assets/icons/calendar.svg index e8aa0d3c..1cd84ac5 100644 --- a/src/assets/icons/calendar.svg +++ b/src/assets/icons/calendar.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + diff --git a/src/components/Calendar/calendarComponent.scss b/src/components/Calendar/calendarComponent.scss index 4beb3afe..8f980833 100644 --- a/src/components/Calendar/calendarComponent.scss +++ b/src/components/Calendar/calendarComponent.scss @@ -160,12 +160,17 @@ a { color: black; + display: flex; + align-items: center; + justify-content: center; + font-weight: 500; + font-size: 12px; } img { width: 16px; height: 16px; - margin: 0 10px 0 0; + margin: 0 5px 0 0; @media (max-width: 968px) { margin-right: 2px; diff --git a/src/components/Modal/Tracker/ModalTicket/ModalTicket.jsx b/src/components/Modal/Tracker/ModalTicket/ModalTicket.jsx index c1d86bfe..f2d14230 100644 --- a/src/components/Modal/Tracker/ModalTicket/ModalTicket.jsx +++ b/src/components/Modal/Tracker/ModalTicket/ModalTicket.jsx @@ -25,6 +25,7 @@ import file from "assets/icons/fileModal.svg"; import link from "assets/icons/link.svg"; import send from "assets/icons/send.svg"; import watch from "assets/icons/watch.svg"; +import avatarMok from "assets/images/avatarMok.png"; import "./modalTicket.scss"; @@ -69,7 +70,7 @@ export const ModalTiсket = ({ task_id: task.id, status: 0, }, - }).then((res) => { + }).then(() => { setActive(false); dispatch(setProjectBoardFetch(projectId)); }); @@ -334,9 +335,9 @@ export const ModalTiсket = ({ ); }, [members]); - function copyProjectLink() { + function copyTicketLink() { navigator.clipboard.writeText( - `https://itguild.info/tracker/project/${projectId}` + `https://itguild.info/tracker/task/${task.id}` ); } @@ -469,7 +470,12 @@ export const ModalTiсket = ({ {executor ? (

Исполнитель: {executor.fio}

- avatar + avatar taskExecutor(person)} > {person.user.fio} - + avatar
); })} @@ -517,7 +530,14 @@ export const ModalTiсket = ({ return (

{member.fio}

- + avatar addMember(person)} > {person.user.fio} - + avatar
); }) @@ -620,7 +647,7 @@ export const ModalTiсket = ({
-

ссылка на проект

+

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

diff --git a/src/components/Modal/Tracker/ModalTicket/modalTicket.scss b/src/components/Modal/Tracker/ModalTicket/modalTicket.scss index ac7925b8..9a1e24ca 100644 --- a/src/components/Modal/Tracker/ModalTicket/modalTicket.scss +++ b/src/components/Modal/Tracker/ModalTicket/modalTicket.scss @@ -129,6 +129,10 @@ width: 100%; position: relative; + .ck-editor { + margin: 10px 0; + } + &__subComment { &:before { content: ""; @@ -251,6 +255,11 @@ } } } + .comment__edit--open { + &:after { + display: none; + } + } } } @@ -508,7 +517,6 @@ &__creator { font-size: 14px; - line-height: 32px; font-weight: 500; color: #2d4a17; max-width: 200px; diff --git a/src/components/Modal/Tracker/TicketFullScreen/TicketFullScreen.jsx b/src/components/Modal/Tracker/TicketFullScreen/TicketFullScreen.jsx index 676a0ed0..a5e264f4 100644 --- a/src/components/Modal/Tracker/TicketFullScreen/TicketFullScreen.jsx +++ b/src/components/Modal/Tracker/TicketFullScreen/TicketFullScreen.jsx @@ -1,3 +1,5 @@ +import ClassicEditor from "@ckeditor/ckeditor5-build-classic"; +import { CKEditor } from "@ckeditor/ckeditor5-react"; import React, { useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { Link, useNavigate, useParams } from "react-router-dom"; @@ -5,7 +7,6 @@ import { Link, useNavigate, useParams } from "react-router-dom"; import { deletePersonOnProject, getBoarderLoader, - getProjectBoard, modalToggle, setProjectBoardFetch, setToggleTab, @@ -27,17 +28,17 @@ import TrackerTaskComment from "@components/TrackerTaskComment/TrackerTaskCommen import archive from "assets/icons/archive.svg"; import archive2 from "assets/icons/archive.svg"; import arrow from "assets/icons/arrows/arrowCalendar.png"; -import arrow2 from "assets/icons/arrows/arrowStart.png"; +import arrowStart from "assets/icons/arrows/arrowStart.png"; import close from "assets/icons/close.png"; 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 plus from "assets/icons/plus.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 avatarMok from "assets/images/avatarMok.png"; import "./ticketFullScreen.scss"; @@ -46,8 +47,8 @@ export const TicketFullScreen = () => { const ticketId = useParams(); const dispatch = useDispatch(); const navigate = useNavigate(); - const projectBoard = useSelector(getProjectBoard); const boardLoader = useSelector(getBoarderLoader); + const [projectInfo, setProjectInfo] = useState({}); const [taskInfo, setTaskInfo] = useState({}); const [editOpen, setEditOpen] = useState(false); const [inputsValue, setInputsValue] = useState({}); @@ -56,6 +57,16 @@ export const TicketFullScreen = () => { const [personListOpen, setPersonListOpen] = useState(false); const [timerStart, setTimerStart] = useState(false); const [timerInfo, setTimerInfo] = useState({}); + const [currentTimerCount, setCurrentTimerCount] = useState({ + hours: 0, + minute: 0, + seconds: 0, + }); + const [timerId, setTimerId] = useState(null); + const [dropListOpen, setDropListOpen] = useState(false); + const [correctProjectUsers, setCorrectProjectUsers] = useState([]); + const [dropListMembersOpen, setDropListMembersOpen] = useState(false); + const [users, setUsers] = useState([]); useEffect(() => { apiRequest(`/task/get-task?task_id=${ticketId.id}`).then((taskInfo) => { @@ -80,13 +91,34 @@ export const TicketFullScreen = () => { }, []); setComments(comments); }); - taskInfo.timers.forEach((time) => { - if (!time.stopped_at) { - setTimerStart(true); - setTimerInfo(time); - } + 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` + ).then((res) => { + setProjectInfo(res); + setCorrectProjectUsers(res.projectUsers); }); - dispatch(setProjectBoardFetch(taskInfo.project_id)); setLoader(boardLoader); }); }, []); @@ -142,6 +174,7 @@ export const TicketFullScreen = () => { }).then((res) => { setTimerStart(true); setTimerInfo(res); + startTimer(); }); } @@ -152,14 +185,17 @@ export const TicketFullScreen = () => { timer_id: timerInfo.id, stopped_at: getCorrectRequestDate(new Date()), }, - }).then(() => setTimerStart(false)); + }).then(() => { + setTimerStart(false); + clearInterval(timerId); + }); } function deletePerson(userId) { apiRequest("/project/del-user", { method: "DELETE", data: { - project_id: projectBoard.id, + project_id: projectInfo.id, user_id: userId, }, }).then(() => { @@ -216,6 +252,119 @@ export const TicketFullScreen = () => { ); } + 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], + })); + }); + } + return (
@@ -265,7 +414,7 @@ export const TicketFullScreen = () => {
-
Проект : {projectBoard.name}
+
Проект : {projectInfo.name}
{ >
- - {projectBoard.projectUsers?.length} - + {projectInfo.projectUsers?.length > 3 && ( + +1... + )} +
+ {projectInfo.projectUsers?.length && + projectInfo.projectUsers.slice(0, 3).map((person) => { + return ( + avatar + ); + })} +
{ @@ -294,14 +459,14 @@ export const TicketFullScreen = () => { onClick={() => setPersonListOpen(false)} />
- {projectBoard.projectUsers?.length} + {projectInfo.projectUsers?.length} участник
- В проекте - “{projectBoard.name}” + В проекте - “{projectInfo.name}”
- {projectBoard.projectUsers?.map((person) => { + {projectInfo.projectUsers?.map((person) => { return (
{ > avatar {person.user.fio} @@ -365,32 +534,52 @@ export const TicketFullScreen = () => { )}
{editOpen ? ( - { + { + const data = editor.getData(); setInputsValue((prevValue) => ({ ...prevValue, - description: e.target.value, + description: data, })); }} /> ) : ( -

{inputsValue.description}

+

)}

-

- { - dispatch(modalToggle("addSubtask")); - setModalAddWorker(true); - }} - styles={"button-green-add"} - > - - Добавить под задачу - -

+ {/*

*/} + {/* {*/} + {/* dispatch(modalToggle("addSubtask"));*/} + {/* setModalAddWorker(true);*/} + {/* }}*/} + {/* styles={"button-green-add"}*/} + {/* >*/} + {/* */} + {/* Добавить под задачу*/} + {/* */} + {/*

*/}

@@ -430,58 +619,159 @@ export const TicketFullScreen = () => {

-
+

- Создатель : {taskInfo.user?.fio} + Создатель :
+ {taskInfo.user?.fio}

-
- {Boolean(taskInfo.taskUsers?.length) && - taskInfo.taskUsers.map((worker, index) => { - return ( -
- worket -

{worker.name}

-
- ); - })} -
+ {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)} + /> +
+ ); + })} +
+
+ )}
- { - dispatch(modalToggle("addWorker")); - setModalAddWorker(true); - }} - styles={"button-add-worker"} +
-
- { - dispatch(modalToggle("addWorker")); - setModalAddWorker(true); - }} - styles={"button-add-worker"} - > - + - - + Добавить участников + {dropListMembersOpen && ( +
+ setDropListMembersOpen(false)} + /> + {users.length ? ( + users.map((person) => { + return ( +
addMember(person)} + > + {person.user.fio} + avatar +
+ ); + }) + ) : ( +

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

+ )} +
+ )}
- watch + Длительность : -

{"0:00:00"}

+

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

{timerStart ? ( - ) : ( @@ -494,7 +784,8 @@ export const TicketFullScreen = () => { } onClick={() => startTaskTimer()} > - Начать делать arrow + Начать делать + )}
diff --git a/src/components/Modal/Tracker/TrackerModal/TrackerModal.jsx b/src/components/Modal/Tracker/TrackerModal/TrackerModal.jsx index 99475330..8a9db262 100644 --- a/src/components/Modal/Tracker/TrackerModal/TrackerModal.jsx +++ b/src/components/Modal/Tracker/TrackerModal/TrackerModal.jsx @@ -1,3 +1,5 @@ +import ClassicEditor from "@ckeditor/ckeditor5-build-classic"; +import { CKEditor } from "@ckeditor/ckeditor5-react"; import React, { useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; @@ -25,6 +27,7 @@ import BaseButton from "@components/Common/BaseButton/BaseButton"; import ModalLayout from "@components/Common/ModalLayout/ModalLayout"; import arrowDown from "assets/icons/arrows/selectArrow.png"; +import avatarMok from "assets/images/avatarMok.png"; import "./trackerModal.scss"; @@ -50,7 +53,7 @@ export const TrackerModal = ({ const [valueColumn, setValueColumn] = useState(""); const [nameProject, setNameProject] = useState(""); const [valueTiket, setValueTiket] = useState(""); - const [descriptionTicket, setDescriptionTicket] = useState(""); + const [descriptionTicket, setDescriptionTicket] = useState("Описание задачи"); const [workers, setWorkers] = useState([]); const [selectWorkersOpen, setSelectWorkersOpen] = useState(false); const [selectedWorker, setSelectedWorker] = useState(null); @@ -114,13 +117,13 @@ export const TrackerModal = ({ dispatch(setProjectBoardFetch(projectBoard.id)); setActive(false); setValueTiket(""); - setDescriptionTicket(""); + setDescriptionTicket("Описание задачи"); setSelectedExecutorTask("Выберите исполнителя задачи"); }); } else { setActive(false); setValueTiket(""); - setDescriptionTicket(""); + setDescriptionTicket("Описание задачи"); dispatch(setProjectBoardFetch(projectBoard.id)); } }); @@ -337,14 +340,27 @@ export const TrackerModal = ({ placeholder="Название задачи" />
-
- setDescriptionTicket(e.target.value)} - placeholder="Описание задачи" - /> -
+ { + const data = editor.getData(); + setDescriptionTicket(data); + }} + />
setSelectExecutorTaskOpen(!selectExecutorTaskOpen)} className={ @@ -381,7 +397,11 @@ export const TrackerModal = ({ {person.user.fio} avatar
diff --git a/src/components/Modal/Tracker/TrackerModal/trackerModal.scss b/src/components/Modal/Tracker/TrackerModal/trackerModal.scss index f8eb408d..5c28876f 100644 --- a/src/components/Modal/Tracker/TrackerModal/trackerModal.scss +++ b/src/components/Modal/Tracker/TrackerModal/trackerModal.scss @@ -31,6 +31,7 @@ align-items: center; flex-direction: column; margin: 0 0 15px 0; + row-gap: 5px; .select-priority { background-color: white; @@ -78,7 +79,7 @@ } .input-container { - width: 287px; + width: 320px; height: 35px; background: #ffffff; border-radius: 8px; @@ -89,8 +90,12 @@ } } + .ck-editor { + max-width: 320px; + } + .select__executor { - width: 287px; + width: 320px; background: white; border-radius: 8px; margin: 5px 0; diff --git a/src/components/TrackerTaskComment/TrackerTaskComment.jsx b/src/components/TrackerTaskComment/TrackerTaskComment.jsx index 30f487e4..b7227a88 100644 --- a/src/components/TrackerTaskComment/TrackerTaskComment.jsx +++ b/src/components/TrackerTaskComment/TrackerTaskComment.jsx @@ -1,3 +1,5 @@ +import ClassicEditor from "@ckeditor/ckeditor5-build-classic"; +import { CKEditor } from "@ckeditor/ckeditor5-react"; import React, { useState } from "react"; import { urlForLocal } from "@utils/helper"; @@ -76,6 +78,7 @@ export const TrackerTaskComment = ({ ? "comments__list__item__main" : "", "comments__list__item", + commentsEditOpen ? "comment__edit--open" : "", comment.parent_id ? "comments__list__item__subComment" : "", ].join(" ")} > @@ -106,17 +109,35 @@ export const TrackerTaskComment = ({
{commentsEditOpen ? ( - { - setCommentsEditText(e.target.value); + { + const data = editor.getData(); + setCommentsEditText(data); }} /> ) : ( -

{commentsEditText}

+

)} - {!comment.parent_id && ( + {!comment.parent_id && !commentsEditOpen && ( <> {subCommentsCreateOpen ? (

diff --git a/src/pages/ProjectTracker/ProjectTracker.js b/src/pages/ProjectTracker/ProjectTracker.js index 8982f294..134ff358 100644 --- a/src/pages/ProjectTracker/ProjectTracker.js +++ b/src/pages/ProjectTracker/ProjectTracker.js @@ -322,29 +322,24 @@ export const ProjectTracker = () => {

добавить колонку

- - {projectBoard.projectUsers?.length} - + {projectBoard.projectUsers?.length > 3 && ( + +1... + )}
{projectBoard.projectUsers?.length && - projectBoard.projectUsers - .slice( - 0, - projectBoard.length > 3 ? 3 : projectBoard.length - ) - .map((person) => { - return ( - avatar - ); - })} + projectBoard.projectUsers.slice(0, 3).map((person) => { + return ( + avatar + ); + })}
{ > avatar {person.user.fio} @@ -462,7 +461,13 @@ export const ProjectTracker = () => { - selectedTabTask(column.id, column.tasks.length) + selectedTabTask( + column.id, + projectBoard?.columns + ? projectBoard?.columns[0].tasks.at(-1) + .priority + 1 + : 1 + ) } > + diff --git a/src/pages/Tracker/tracker.scss b/src/pages/Tracker/tracker.scss index 48764b45..1a38bead 100644 --- a/src/pages/Tracker/tracker.scss +++ b/src/pages/Tracker/tracker.scss @@ -295,7 +295,7 @@ color: #252c32; border: 1px solid #dde2e4; background: white; - left: -5px; + left: -6px; } .addPerson { @@ -329,9 +329,11 @@ &__close { cursor: pointer; position: absolute; - right: 20px; - top: 15px; + right: 25px; + top: 25px; margin-left: auto; + width: 35px; + height: 35px; } &__count { @@ -356,7 +358,7 @@ line-height: 22px; color: #263238; font-weight: 500; - margin: 13px 0 32px; + margin: 13px 0 10px; span { width: auto;