This commit is contained in:
Николай Полтщук 2023-06-12 23:30:18 +03:00
parent 5c742411d5
commit 9b99da9261
9 changed files with 1014 additions and 460 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

View File

@ -1,48 +1,45 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Link, useNavigate, useParams } from "react-router-dom";
import { ProfileHeader } from "../../../ProfileHeader/ProfileHeader";
import { ProfileBreadcrumbs } from "../../../ProfileBreadcrumbs/ProfileBreadcrumbs";
import { Footer } from "@components/Common/Footer/Footer";
import { Link, useParams, useNavigate } from "react-router-dom";
import TrackerModal from "../../../Modal/TrackerModal/TrackerModal";
import TrackerTaskComment from "../../../TrackerTaskComment/TrackerTaskComment";
import { Navigation } from "../../../Navigation/Navigation";
import {Loader} from "@components/Common/Loader/Loader";
import {useDispatch, useSelector} from "react-redux";
import { import {
deletePersonOnProject, deletePersonOnProject,
getBoarderLoader,
getProjectBoard,
modalToggle, modalToggle,
setProjectBoardFetch, setProjectBoardFetch,
setToggleTab, setToggleTab,
} from "@redux/projectsTrackerSlice"; getProjectBoard,
getBoarderLoader,
} from "../../../../redux/projectsTrackerSlice";
import { apiRequest } from "../../../../api/request";
import { urlForLocal } from "@utils/helper"; import project from "../../../../assets/icons/trackerProject.svg";
import watch from "../../../../assets/icons/watch.svg";
import { apiRequest } from "@api/request"; import file from "../../../../assets/icons/fileModal.svg";
import send from "../../../../assets/icons/send.svg";
import { getCorrectDate } from "@components/Calendar/calendarHelper"; import arrow2 from "../../../../assets/icons/arrows/arrowStart.png";
import BaseButton from "@components/Common/BaseButton/BaseButton"; import plus from "../../../../assets/icons/plus.svg";
import { Footer } from "@components/Common/Footer/Footer"; import tasks from "../../../../assets/icons/trackerTasks.svg";
import { Loader } from "@components/Common/Loader/Loader"; import archive from "../../../../assets/icons/archive.svg";
import TrackerModal from "@components/Modal/TrackerModal/TrackerModal"; import arrow from "../../../../assets/icons/arrows/arrowCalendar.png";
import { Navigation } from "@components/Navigation/Navigation"; import link from "../../../../assets/icons/link.svg";
import { ProfileBreadcrumbs } from "@components/ProfileBreadcrumbs/ProfileBreadcrumbs"; import archive2 from "../../../../assets/icons/archive.svg";
import { ProfileHeader } from "@components/ProfileHeader/ProfileHeader"; import del from "../../../../assets/icons/delete.svg";
import edit from "../../../../assets/icons/edit.svg";
import archive2 from "assets/icons/archive.svg"; import close from "../../../../assets/icons/close.png";
import archive from "assets/icons/archiveTracker.svg";
import arrow from "assets/icons/arrows/arrowCalendar.png";
import arrow2 from "assets/icons/arrows/arrowStart.png";
import selectArrow from "assets/icons/arrows/select.svg";
import close from "assets/icons/closeProjectPersons.svg";
import del from "assets/icons/delete.svg";
import edit from "assets/icons/edit.svg";
import file from "assets/icons/fileModal.svg";
import link from "assets/icons/link.svg";
import 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 "./ticketFullScreen.scss"; import "./ticketFullScreen.scss";
export const TicketFullScreen = () => { import {getCorrectRequestDate, urlForLocal} from "../../../../utils/helper";
export const TicketFullScreen = ({}) => {
const [modalAddWorker, setModalAddWorker] = useState(false); const [modalAddWorker, setModalAddWorker] = useState(false);
const ticketId = useParams(); const ticketId = useParams();
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -54,35 +51,35 @@ export const TicketFullScreen = () => {
const [inputsValue, setInputsValue] = useState({}); const [inputsValue, setInputsValue] = useState({});
const [loader, setLoader] = useState(true); const [loader, setLoader] = useState(true);
const [comments, setComments] = useState([]); const [comments, setComments] = useState([]);
const [commentsEditOpen, setCommentsEditOpen] = useState({}); const [personListOpen, setPersonListOpen] = useState(false)
const [commentsEditText, setCommentsEditText] = useState({}); const [timerStart, setTimerStart] = useState(false)
const [personListOpen, setPersonListOpen] = useState(false); const [timerInfo, setTimerInfo] = useState({})
useEffect(() => { useEffect(() => {
apiRequest(`/task/get-task?task_id=${ticketId.id}`).then((taskInfo) => { apiRequest(`/task/get-task?task_id=${ticketId.id}`).then((taskInfo) => {
setTaskInfo(taskInfo); setTaskInfo(taskInfo);
setInputsValue({ setInputsValue({title: taskInfo.title, description: taskInfo.description, comment: ''})
title: taskInfo.title, apiRequest(`/comment/get-by-entity?entity_type=2&entity_id=${taskInfo.id}`).then((res) => {
description: taskInfo.description, const comments = res.reduce((acc, cur) => {
comment: "", if (!cur.parent_id) {
}); acc.push({...cur, subComments: []})
apiRequest( } else {
`/comment/get-by-entity?entity_type=2&entity_id=${taskInfo.id}` acc.forEach((item) => {
).then((res) => { if (item.id === cur.parent_id) item.subComments.push(cur)
setComments(res); })
res.forEach((item) => { }
setCommentsEditOpen((prevValue) => ({ return acc
...prevValue, }, [])
[item.id]: false, setComments(comments)
})); })
setCommentsEditText((prevValue) => ({ taskInfo.timers.forEach((time) => {
...prevValue, if (!time.stopped_at) {
[item.id]: item.text, setTimerStart(true)
})); setTimerInfo(time)
}); }
}); })
dispatch(setProjectBoardFetch(taskInfo.project_id)); dispatch(setProjectBoardFetch(taskInfo.project_id));
setLoader(boardLoader); setLoader(boardLoader)
}); });
}, []); }, []);
@ -104,9 +101,10 @@ export const TicketFullScreen = () => {
data: { data: {
task_id: taskInfo.id, task_id: taskInfo.id,
title: inputsValue.title, title: inputsValue.title,
description: inputsValue.description, description: inputsValue.description
}, },
}).then(() => {}); }).then(() => {
});
} }
function createComment() { function createComment() {
@ -115,43 +113,39 @@ export const TicketFullScreen = () => {
data: { data: {
text: inputsValue.comment, text: inputsValue.comment,
entity_type: 2, entity_type: 2,
entity_id: taskInfo.id, entity_id: taskInfo.id
}, }
}).then((res) => { }).then((res) => {
let newComment = res; let newComment = res
newComment.created_at = new Date(); newComment.created_at = new Date()
setInputsValue((prevValue) => ({ ...prevValue, comment: "" })); newComment.subComments = []
setComments((prevValue) => [...prevValue, newComment]); setInputsValue((prevValue) => ({...prevValue, comment: ''}))
setCommentsEditOpen((prevValue) => ({ ...prevValue, [res.id]: false })); setComments((prevValue) => ([...prevValue, newComment]))
setCommentsEditText((prevValue) => ({ })
...prevValue,
[res.id]: res.text,
}));
});
} }
function deleteComment(commentId) { function startTaskTimer() {
apiRequest("/comment/update", { apiRequest("/timer/create", {
method: "PUT", method: "POST",
data: { data: {
comment_id: commentId, entity_type: 2,
status: 0, entity_id: taskInfo.id,
}, created_at: getCorrectRequestDate(new Date())
}).then(() => { }
setComments((prevValue) => }).then((res) => {
prevValue.filter((item) => item.id !== commentId) setTimerStart(true)
); setTimerInfo(res)
}); })
} }
function editComment(commentId) { function stopTaskTimer() {
apiRequest("/comment/update", { apiRequest("/timer/update", {
method: "PUT", method: "PUT",
data: { data: {
comment_id: commentId, timer_id: timerInfo.id,
text: commentsEditText[commentId], stopped_at: getCorrectRequestDate(new Date())
}, }
}).then(() => {}); }).then(() => setTimerStart(false))
} }
function deletePerson(userId) { function deletePerson(userId) {
@ -159,364 +153,304 @@ export const TicketFullScreen = () => {
method: "DELETE", method: "DELETE",
data: { data: {
project_id: projectBoard.id, project_id: projectBoard.id,
user_id: userId, user_id: userId
}, },
}).then(() => { }).then(() => {
dispatch(deletePersonOnProject(userId)); dispatch(deletePersonOnProject(userId))
}); });
} }
function commentDelete(comment) {
setComments((prevValue) => prevValue.filter((item) => item.id !== comment.id))
if (comment.subComments.length) {
comment.subComments.forEach((subComment) => {
apiRequest("/comment/update", {
method: "PUT",
data: {
comment_id: subComment.id,
status: 0
}
}).then(() => {
})
})
}
}
function addSubComment(commentId, subComment) {
const addSubComment = comments
addSubComment.forEach((comment) => {
if (comment.id === commentId) {
comment.subComments.push(subComment)
}
})
setComments(addSubComment)
}
function subCommentDelete(subComment) {
const deleteSubComment = comments
deleteSubComment.forEach((comment, index) => {
if (comment.id === subComment.parent_id) {
deleteSubComment[index].subComments = comment.subComments.filter((item) => item.id !== subComment.id)
}
})
setComments([...deleteSubComment])
}
const toggleTabs = (index) => { const toggleTabs = (index) => {
dispatch(setToggleTab(index)); dispatch(setToggleTab(index));
}; };
return ( return (
<section className="ticket-full-screen"> <section className="ticket-full-screen">
<ProfileHeader /> <ProfileHeader />
<Navigation /> <Navigation />
<div className="container"> <div className="container">
<div className="tracker__content"> <div className="tracker__content">
<ProfileBreadcrumbs <ProfileBreadcrumbs
links={[ links={[
{ name: "Главная", link: "/profile" }, { name: "Главная", link: "/profile" },
{ name: "Трекер", link: "/profile/tracker" }, { name: "Трекер", link: "/profile/tracker" },
]} ]}
/> />
<h2 className="tracker__title">Управление проектами с трекером</h2> <h2 className="tracker__title">Управление проектами с трекером</h2>
</div>
</div> </div>
</div> <div className="tracker__tabs">
<div className="tracker__tabs"> <div className="tracker__tabs__head">
<div className="tracker__tabs__head"> <Link
<Link to="/profile/tracker"
to="/profile/tracker" className="tab active-tab"
className="tab active-tab" onClick={() => toggleTabs(1)}
onClick={() => toggleTabs(1)} >
> <img src={project} alt="img" />
<img src={project} alt="img" /> <p>Проекты </p>
<p>Проекты </p> </Link>
</Link> <Link
<Link to="/profile/tracker"
to="/profile/tracker" className="tab"
className="tab" onClick={() => toggleTabs(2)}
onClick={() => toggleTabs(2)} >
> <img src={tasks} alt="img" />
<img src={tasks} alt="img" /> <p>Все мои задачи</p>
<p>Все мои задачи</p> </Link>
</Link> <Link
<Link to="/profile/tracker"
to="/profile/tracker" className="tab"
className="tab" onClick={() => toggleTabs(3)}
onClick={() => toggleTabs(3)} >
> <img src={archive} alt="img" />
<img src={archive} alt="img" /> <p>Архив</p>
<p>Архив</p> </Link>
</Link> </div>
</div> {loader ? <Loader /> :
{loader ? ( <>
<Loader /> <div className="tracker__tabs__content content-tabs">
) : ( <div className="tasks__head">
<> <div className="tasks__head__wrapper">
<div className="tracker__tabs__content content-tabs"> <h4>Проект : {projectBoard.name}</h4>
<div className="tasks__head">
<div className="tasks__head__wrapper">
<h4>Проект : {projectBoard.name}</h4>
<TrackerModal <TrackerModal
active={modalAddWorker} active={modalAddWorker}
setActive={setModalAddWorker} setActive={setModalAddWorker}
></TrackerModal> ></TrackerModal>
<div className="tasks__head__persons"> <div className="tasks__head__persons">
{/*<img src={avatarTest} alt="avatar" />*/} {/*<img src={avatarTest} alt="avatar" />*/}
{/*<img src={avatarTest} alt="avatar" />*/} {/*<img src={avatarTest} alt="avatar" />*/}
<span className="countPersons"> <span className="countPersons">{projectBoard.projectUsers?.length}</span>
{projectBoard.projectUsers?.length} <span
</span> className="addPerson"
<span onClick={() => {
className="addPerson" setPersonListOpen(true)
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">
<span>{projectBoard.projectUsers?.length}</span>
участник
</div>
<div className="persons__list__info">
В проекте - <span>{projectBoard.name}</span>
</div>
<div className="persons__list__items">
{projectBoard.projectUsers?.map((person) => {
return (
<div
className="persons__list__item"
key={person.user_id}
>
<img
className="avatar"
src={urlForLocal(person.user.avatar)}
alt="avatar"
/>
<span>{person.user.fio}</span>
<img
className="delete"
src={close}
alt="delete"
onClick={() => deletePerson(person.user_id)}
/>
</div>
);
})}
</div>
<div
className="persons__list__add"
onClick={() => {
dispatch(modalToggle("addWorker"));
setModalAddWorker(true);
setPersonListOpen(false);
}}
> >
<span className="addPerson">+</span> +
<p>Добавить участников</p> </span>
</div> <p>добавить участника</p>
</div> {personListOpen &&
)} <div className='persons__list'>
</div> <img className='persons__list__close' src={close} alt='close' onClick={() => setPersonListOpen(false)} />
<div className="tasks__head__select"> <div className='persons__list__count'><span>{projectBoard.projectUsers?.length}</span>участник</div>
<span>Учавствую</span> <div className='persons__list__info'>В проекте - <span>{projectBoard.name}</span></div>
<img src={selectArrow} alt="arrow" /> <div className='persons__list__items'>
</div> {projectBoard.projectUsers?.map((person) => {
<div className="tasks__head__select"> return <div className='persons__list__item' key={person.user_id}>
<span>Мои</span> <img className='avatar' src={urlForLocal(person.user.avatar)} alt='avatar' />
<img src={selectArrow} alt="arrow" /> <span>{person.user.fio}</span>
</div> <img className='delete' src={close} alt='delete' onClick={() => deletePerson(person.user_id)}/>
<Link to={`/profile/tracker`} className="link"> </div>
<div className="tasks__head__back"> })
<p>Вернуться на проекты</p> }
<img src={arrow} alt="arrow" /> </div>
</div> <div className='persons__list__add'
</Link> onClick={() => {
</div> dispatch(modalToggle("addWorker"));
</div> setModalAddWorker(true);
</div> setPersonListOpen(false)
<div className="tracker-ticket ticket"> }}
<div className="content ticket-whith"> >
<div className="content__task"> <span className='addPerson'>+</span>
<span>Задача</span> <p>Добавить участников</p>
{editOpen ? ( </div>
<input
value={inputsValue.title}
onChange={(e) => {
setInputsValue((prevValue) => ({
...prevValue,
title: e.target.value,
}));
}}
/>
) : (
<h5>{inputsValue.title}</h5>
)}
<div className="content__description">
{editOpen ? (
<input
value={inputsValue.description}
onChange={(e) => {
setInputsValue((prevValue) => ({
...prevValue,
description: e.target.value,
}));
}}
/>
) : (
<p>{inputsValue.description}</p>
)}
</div>
<div className="content__communication">
<p className="tasks">
<BaseButton
onClick={() => {
dispatch(modalToggle("addSubtask"));
setAddSubtask(true);
}}
styles={"tasks__button"}
>
<img src={plus}></img>
Добавить под задачу
</BaseButton>
</p>
<p className="file">
<BaseButton styles={"file__button"}>
<img src={file}></img>
Загрузить файл
</BaseButton>
<span>{0}</span>
Файлов
</p>
</div>
<div className="content__input">
<input
placeholder="Оставить комментарий"
value={inputsValue.comment}
onChange={(e) => {
setInputsValue((prevValue) => ({
...prevValue,
comment: e.target.value,
}));
}}
/>
<img src={send} onClick={createComment}></img>
</div>
<div className="comments__list">
{comments.map((comment) => {
return (
<div className="comments__list__item" key={comment.id}>
<div className="comments__list__item__info">
<span>{getCorrectDate(comment.created_at)}</span>
<div
className={
commentsEditOpen[comment.id]
? "edit edit__open"
: "edit"
}
>
<img
src={edit}
alt="edit"
onClick={() => {
if (commentsEditOpen[comment.id]) {
editComment(comment.id);
}
setCommentsEditOpen((prevValue) => ({
...prevValue,
[comment.id]: !prevValue[comment.id],
}));
}}
/>
</div> </div>
<img }
src={del} </div>
alt="delete" <Link to={`/profile/tracker`} className="link">
onClick={() => deleteComment(comment.id)} <div className="tasks__head__back">
/> <p>Вернуться на проекты</p>
</div> <img src={arrow} alt="arrow" />
{commentsEditOpen[comment.id] ? (
<input
value={commentsEditText[comment.id]}
onChange={(e) => {
setCommentsEditText((prevValue) => ({
...prevValue,
[comment.id]: e.target.value,
}));
}}
/>
) : (
<p>{commentsEditText[comment.id]}</p>
)}
</div> </div>
); </Link>
})} </div>
</div> </div>
</div> </div>
</div> <div className="modal-tiket__content ticket">
<div className="workers"> <div className="content ticket-whith">
<div className="workers_box"> <div className="content__task">
<p className="workers__creator"> <span>Задача</span>
Создатель : <span>{taskInfo.user?.fio}</span> {editOpen ? <input value={inputsValue.title} onChange={(e) => {
</p> setInputsValue((prevValue) => ({...prevValue, title: e.target.value}))
<div> }} /> :<h5>{inputsValue.title}</h5>}
{Boolean(taskInfo.taskUsers?.length) && <div className="content__description">
taskInfo.taskUsers.map((worker, index) => { {editOpen ? <input value={inputsValue.description} onChange={(e) => {
return ( setInputsValue((prevValue) => ({...prevValue, description: e.target.value}))
<div className="worker" key={index}> }}/> :<p>{inputsValue.description}</p>}
<img src={worker.avatar}></img> {/*<img src={task} className="image-task"></img>*/}
<p>{worker.name}</p> </div>
</div> <div className="content__communication">
); <p className="tasks">
})} <button
</div> onClick={() => {
dispatch(modalToggle("addSubtask"));
setModalAddWorker(true);
}}
>
<img src={plus} alt='plus'></img>
Добавить под задачу
</button>
</p>
<p className="file">
<button>
<img src={file} alt='file'></img>
Загрузить файл
</button>
<span>{0}</span>
Файлов
</p>
</div>
<div className="content__input">
<input placeholder="Оставить комментарий" value={inputsValue.comment} onChange={(e) => {
setInputsValue((prevValue) => ({...prevValue, comment: e.target.value}))
}} />
<img src={send} onClick={createComment} alt='send'></img>
</div>
<div className='comments__list'>
{comments.map((comment) => {
return <TrackerTaskComment
key={comment.id}
taskId={taskInfo.id}
comment={comment}
commentDelete={commentDelete}
addSubComment={addSubComment}
subCommentDelete={subCommentDelete}
/>
})
<div className="add-worker moreItems"> }
<button </div>
onClick={() => { </div>
dispatch(modalToggle("addWorker"));
setModalAddWorker(true);
}}
>
+
</button>
<span>Добавить исполнителя</span>
</div> </div>
<div className="add-worker moreItems"> <div className="workers">
<button <div className="workers_box">
onClick={() => { <p className="workers__creator">
dispatch(modalToggle("addWorker")); Создатель : <span>{taskInfo.user?.fio}</span>
setModalAddWorker(true); </p>
}} <div>
> {Boolean(taskInfo.taskUsers?.length) &&
+ taskInfo.taskUsers.map((worker, index) => {
</button> return (
<span>Добавить участников</span> <div className="worker" key={index}>
</div> <img src={worker.avatar} alt='worket'></img>
</div> <p>{worker.name}</p>
</div>
);
})}
</div>
<div className="workers_box-middle"> <div className="add-worker moreItems">
<div className="time"> <button
<img src={watch}></img> onClick={() => {
<span>Длительность : </span> dispatch(modalToggle("addWorker"));
<p>{"0:00:00"}</p> setModalAddWorker(true);
</div> }}
>
+
</button>
<span>Добавить исполнителя</span>
</div>
<div className="add-worker moreItems">
<button
onClick={() => {
dispatch(modalToggle("addWorker"));
setModalAddWorker(true);
}}
>
+
</button>
<span>Добавить участников</span>
</div>
</div>
<button className="start"> <div className="workers_box-middle">
Начать делать <img src={arrow2}></img> <div className="time">
</button> <img src={watch} alt='watch'></img>
</div> <span>Длительность : </span>
<p>{"0:00:00"}</p>
</div>
<div className="workers_box-bottom"> {timerStart ?
<div <button className="stop" onClick={() => stopTaskTimer()}>
className={editOpen ? "edit" : ""} Остановить
onClick={() => { </button>
if (editOpen) { :
setEditOpen(!editOpen); <button className={taskInfo.executor_id === Number(localStorage.getItem('id')) ? 'start' : 'start disable'} onClick={() => startTaskTimer()}>
editTask(); Начать делать <img src={arrow2} alt='arrow'></img>
} else { </button>
setEditOpen(!editOpen);
} }
}} </div>
>
<img src={edit}></img> <div className="workers_box-bottom">
<p>{editOpen ? "сохранить" : "редактировать"}</p> <div className={editOpen ? 'edit' : ''} onClick={() => {
</div> if(editOpen) {
<div> setEditOpen(!editOpen)
<img src={link}></img> editTask()
<p>ссылка на проект</p> } else {
</div> setEditOpen(!editOpen)
<div> }
<img src={archive2}></img> }}>
<p>в архив</p> <img src={edit} alt='edit'></img>
</div> <p>{editOpen ? 'сохранить' : 'редактировать'}</p>
<div onClick={deleteTask}> </div>
<img src={del}></img> <div>
<p>удалить</p> <img src={link} alt='link'></img>
<p>ссылка на проект</p>
</div>
<div>
<img src={archive2} alt='arch'></img>
<p>в архив</p>
</div>
<div onClick={deleteTask}>
<img src={del} alt='delete'></img>
<p>удалить</p>
</div>
</div>
</div> </div>
</div> </div>
</div> </>
</div> }
</> </div>
)} <Footer />
</div> </section>
<Footer />
</section>
); );
}; };

View File

@ -2,7 +2,7 @@ import React, {useEffect, useState} from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { apiRequest } from "../../../api/request"; import { apiRequest } from "../../../api/request";
import { urlForLocal } from '../../../helper' import { urlForLocal } from '../../../utils/helper'
import { import {
setColumnName, setColumnName,
setColumnPriority, setColumnPriority,
@ -17,7 +17,7 @@ import {
addPersonToProject, getColumnPriority addPersonToProject, getColumnPriority
} from "../../../redux/projectsTrackerSlice"; } from "../../../redux/projectsTrackerSlice";
import arrowDown from "../../../images/selectArrow.png" import arrowDown from "../../../assets/icons/arrows/selectArrow.png"
import "./trackerModal.scss"; import "./trackerModal.scss";

View File

@ -1,11 +1,11 @@
import React, { useState } from "react"; import React, { useState } from "react";
import TrackerTaskSubComment from "../TrackerTaskComment/TrackerTaskComment"; import TrackerTaskSubComment from "../TrackerTaskComment/TrackerTaskComment";
import { apiRequest } from "../../api/request"; import { apiRequest } from "../../api/request";
import {urlForLocal} from "../../helper"; import {urlForLocal} from '../../utils/helper';
import {getCorrectDate} from "../Calendar/calendarHelper"; import {getCorrectDate} from "../Calendar/calendarHelper";
import edit from "../../images/edit.svg"; import edit from "../../assets/icons/edit.svg";
import del from "../../images/delete.svg"; import del from "../../assets/icons/delete.svg";
import accept from "../../images/accept.png"; import accept from "../../assets/images/accept.png";
export const TrackerTaskComment = ({ export const TrackerTaskComment = ({
taskId, taskId,

View File

@ -1,29 +1,29 @@
import React, {useEffect, useState} from "react"; import React, {useEffect, useState} from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import TrackerModal from "../TrackerModal/TrackerModal"; import TrackerModal from "../../../components/Modal/TrackerModal/TrackerModal";
import TrackerTaskComment from "../../../components/TrackerTaskComment/TrackerTaskComment"; import TrackerTaskComment from "../../../components/TrackerTaskComment/TrackerTaskComment";
import { apiRequest } from "../../../api/request"; import { apiRequest } from "../../../api/request";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import "./modalTicket.scss"
import { import {
modalToggle, modalToggle,
setProjectBoardFetch, setProjectBoardFetch,
} from "../../../redux/projectsTrackerSlice"; } from "../../../redux/projectsTrackerSlice";
import category from "../../../images/category.png"; import category from "../../../assets/icons/category.svg";
import watch from "../../../images/watch.png"; import watch from "../../../assets/icons/watch.svg";
import file from "../../../images/fileModal.svg"; import file from "../../../assets/icons/fileModal.svg";
import arrow from "../../../images/arrowStart.png"; import arrow from "../../../assets/icons/arrows/arrowStart.png";
import link from "../../../images/link.svg"; import link from "../../../assets/icons/link.svg";
import archive from "../../../images/archive.svg"; import archive from "../../../assets/icons/archive.svg";
import del from "../../../images/delete.svg"; import del from "../../../assets/icons/delete.svg";
import edit from "../../../images/edit.svg"; import edit from "../../../assets/icons/edit.svg";
import send from "../../../images/send.svg"; import send from "../../../assets/icons/send.svg";
import plus from "../../../images/plus.svg"; import plus from "../../../assets/icons/plus.svg";
import fullScreen from "../../../images/inFullScreen.svg"; import fullScreen from "../../../assets/icons/arrows/inFullScreen.svg";
import close from "../../../images/closeProjectPersons.svg"; import close from "../../../assets/icons/closeProjectPersons.svg";
import "./ModalTicket.scss"; import {urlForLocal, getCorrectRequestDate} from "../../../utils/helper";
import {urlForLocal, getCorrectRequestDate} from "../../../helper";
export const ModalTiсket = ({ export const ModalTiсket = ({
active, active,

View File

@ -0,0 +1,620 @@
.modal-tiket {
z-index: 9;
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, 0.11);
position: fixed;
top: 0;
left: 0;
display: none;
align-items: center;
justify-content: center;
}
.modal-tiket.active {
display: flex;
}
.modal-tiket__content {
background: #ffffff;
//border: 1px solid #dde2e4;
border-radius: 8px;
display: flex;
flex-direction: row;
.content {
display: flex;
flex-direction: column;
width: 600px;
margin: 26px 0 0 21px;
.title-project {
color: #1458dd;
font-family: "LabGrotesque", sans-serif;
display: flex;
align-items: center;
font-weight: 700;
font-size: 22px;
line-height: 32px;
&__category {
margin-right: 17px;
}
&__full {
margin-left: 35%;
}
}
&__task {
margin-top: -5px;
padding: 18px;
display: flex;
flex-direction: column;
input {
font-style: normal;
font-size: 16px;
line-height: 24px;
max-width: 340px;
outline: none;
}
button {
img {
margin-right: 5px;
}
}
h5 {
font-family: "Inter", sans-serif;
font-weight: 500;
font-style: normal;
font-size: 16px;
line-height: 24px;
color: #1a1919;
margin-bottom: 0;
}
.comments__list {
display: flex;
flex-direction: column;
max-height: 420px;
overflow: auto;
&::-webkit-scrollbar {
width: 4px;
border-radius: 10px;
}
&::-webkit-scrollbar-thumb {
background: #cbd9f9;
border-radius: 10px;
}
&::-webkit-scrollbar-track {
background: #c5c0c6;
border-radius: 10px;
}
&__item {
padding: 10px 20px;
display: flex;
flex-direction: column;
max-width: 438px;
border-radius: 44px;
font-size: 14px;
width: 100%;
position: relative;
&__subComment {
&:before {
content: '';
background: #E4E4E6;
height: 1px;
width: 7px;
position: absolute;
top: 36%;
left: 2.5%;
}
}
&__main {
&:after {
content: '';
position: absolute;
background-image: url("../../../assets/images/mainTaskCommentImg.png");
width: 10px;
height: 8px;
top: 50px;
left: 25px;
}
&:before {
content: '';
position: absolute;
background: #E4E4E6;
width: 1px;
height: calc(100% - 120px);
left: 29px;
top: 65px;
}
}
&__fio {
display: flex;
align-items: center;
p {
font-size: 12px;
color: #000000;
line-height: 32px;
max-width: 150px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin-left: 10px;
}
img {
width: 24px;
height: 24px;
}
}
&__date {
display: flex;
align-items: center;
column-gap: 5px;
img {
cursor: pointer;
width: 15px;
}
span {
font-size: 12px;
}
}
&__text {
margin-left: 34px;
}
&__info {
display: flex;
justify-content: space-between;
.edit {
width: 25px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
}
.edit__open {
background: green;
}
}
&__answer {
margin-left: 34px;
text-decoration-line: underline;
font-weight: 400;
font-size: 10px;
line-height: 32px;
cursor: pointer;
&__new {
margin-left: 34px;
font-size: 14px;
border-radius: 5px;
border: 1px solid gainsboro;
padding: 3px 5px;
display: flex;
align-items: center;
margin-top: 5px;
img {
width: 20px;
height: 20px;
cursor: pointer;
}
input {
width: 90%;
border: none;
}
}
}
}
}
}
&__description {
display: flex;
flex-direction: column;
margin-top: 10px;
p {
font-weight: 400;
font-size: 14px;
line-height: 140%;
color: #252c32;
text-align: justify;
}
.image-task {
margin: 10px 0 20px 0;
max-width: 330px;
}
}
&__communication {
display: flex;
flex-direction: row;
margin: 29px 0 0 -5px;
.tasks {
justify-content: space-evenly;
button {
width: 180px;
height: 40px;
background: #52b709;
border-radius: 44px;
font-weight: 400;
font-size: 12px;
line-height: 32px;
border: none;
color: #ffffff;
}
}
.tasks,
.file {
display: flex;
align-items: center;
}
.file {
justify-content: space-between;
margin-left: 20px;
button {
display: flex;
align-items: center;
justify-content: center;
background: white;
width: 166px;
height: 40px;
border: 0.5px solid #1458dd;
border-radius: 44px;
font-weight: 400;
font-size: 12px;
line-height: 32px;
color: #1458dd;
img {
margin-right: 9px;
}
}
span {
margin: 0 3px 0 11px;
font-weight: 500;
font-size: 12px;
line-height: 15px;
color: #6e7c87;
}
}
}
&__input {
margin: 20px 0 20px 0;
display: flex;
align-items: center;
justify-content: space-between;
width: 438px;
height: 40px;
background: #f1f1f1;
border-radius: 44px;
input {
width: 80%;
background: inherit;
border: none;
outline: none;
padding-left: 30px;
font-weight: 400;
font-size: 12px;
line-height: 32px;
border-radius: 44px;
}
img {
cursor: pointer;
margin-right: 18px;
}
}
}
.members {
display: flex;
flex-direction: column;
font-size: 14px;
&__list {
display: flex;
flex-direction: column;
row-gap: 8px;
}
}
.workers {
position: relative;
border-left: 1px solid #f1f1f1;
.exit {
cursor: pointer;
position: absolute;
top: 35px;
right: 40px;
&:before,
&:after {
content: "";
position: absolute;
width: 16px;
height: 2px;
background: #263238;
}
&:before {
transform: rotate(45deg);
}
&:after {
transform: rotate(-45deg);
}
}
span {
font-family: "Inter", sans-serif;
font-weight: 500;
font-size: 11px;
color: #807777;
}
.nameProject {
max-width: 200px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.add-worker {
display: flex;
align-items: center;
position: relative;
span {
color: #000000;
font-size: 12px;
line-height: 32px;
margin-left: 17px;
font-style: normal;
font-weight: 400;
}
button {
cursor: pointer;
background: #8bcc60;
border-radius: 44px;
width: 33px;
height: 33px;
display: flex;
justify-content: center;
align-items: center;
border: none;
color: white;
font-size: 17px;
}
}
.start {
font-size: 12px;
margin-top: 25px;
width: 151px;
height: 40px;
border: none;
color: white;
background: #1458dd;
border-radius: 44px;
img {
margin-left: 10px;
}
}
.disable {
opacity: 0.5;
pointer-events: none;
}
.stop {
font-size: 12px;
margin-top: 25px;
width: 151px;
height: 40px;
border: none;
color: white;
background: red;
border-radius: 44px;
}
.time {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
margin-top: 20px;
width: 160px;
p {
color: #000000;
margin: 0;
}
}
&__creator {
font-size: 14px;
line-height: 32px;
font-weight: 500;
color: #2d4a17;
max-width: 200px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
span {
margin-left: 5px;
font-size: 14px;
line-height: 32px;
font-weight: 500;
display: flex;
}
}
.worker {
display: flex;
flex-direction: row;
align-items: center;
p {
max-width: 170px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 14px;
}
img {
max-width: 25px;
max-height: 25px;
margin-left: 5px;
}
}
.task__info {
display: flex;
flex-direction: column;
row-gap: 5px;
.delete {
cursor: pointer;
}
.executor {
display: flex;
font-size: 14px;
align-items: center;
p {
max-width: 170px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
img {
margin-left: 5px;
max-width: 25px;
max-height: 25px;
}
}
.dropdownList {
position: absolute;
background: white;
padding: 10px;
border-radius: 10px;
top: 0;
z-index: 10;
border: 1px solid gray;
width: 260px;
display: flex;
flex-direction: column;
row-gap: 8px;
.noUsers {
font-size: 14px;
text-align: center;
}
&__close {
cursor: pointer;
margin-left: auto;
width: 12px;
margin-right: 5px;
}
&__person {
display: flex;
justify-content: space-between;
cursor: pointer;
img {
max-width: 30px;
max-height: 30px;
}
}
}
}
&_box {
padding: 25px 85px 40px 40px;
border-bottom: 1px solid #f1f1f1;
&-middle {
padding: 0px 40px 25px 40px;
border-bottom: 1px solid #f1f1f1;
}
&-bottom {
padding: 40px 110px 75px 56px;
font-weight: 400;
font-size: 14px;
line-height: 38px;
div {
display: flex;
cursor: pointer;
align-items: center;
padding-left: 10px;
p {
margin: 0 0 0 12px;
}
}
.edit {
background: #52b709;
border-radius: 50px;
p {
font-weight: 700;
}
}
}
}
}
}
.subtask {
h4 {
width: 90%;
p {
color: #1458dd;
}
}
}

View File

@ -2,12 +2,12 @@ import React, { useEffect, useState } from "react";
import { ProfileHeader } from "../../ProfileHeader/ProfileHeader"; import { ProfileHeader } from "../../ProfileHeader/ProfileHeader";
import { ProfileBreadcrumbs } from "../../ProfileBreadcrumbs/ProfileBreadcrumbs"; import { ProfileBreadcrumbs } from "../../ProfileBreadcrumbs/ProfileBreadcrumbs";
import { Footer } from "../../Footer/Footer"; import { Footer } from "@components/Common/Footer/Footer";
import { Link, useParams, useNavigate } from "react-router-dom"; import { Link, useParams, useNavigate } from "react-router-dom";
import TrackerModal from "../TrackerModal/TrackerModal"; import TrackerModal from "../../Modal/TrackerModal/TrackerModal";
import TrackerTaskComment from "../../../components/TrackerTaskComment/TrackerTaskComment"; import TrackerTaskComment from "../../../components/TrackerTaskComment/TrackerTaskComment";
import { Navigation } from "../../Navigation/Navigation"; import { Navigation } from "../../Navigation/Navigation";
import {Loader} from "../../Loader/Loader"; import {Loader} from "@components/Common/Loader/Loader";
import {useDispatch, useSelector} from "react-redux"; import {useDispatch, useSelector} from "react-redux";
import { import {
@ -33,7 +33,7 @@ import link from "../../../images/link.svg";
import archive2 from "../../../images/archive.svg"; import archive2 from "../../../images/archive.svg";
import del from "../../../images/delete.svg"; import del from "../../../images/delete.svg";
import edit from "../../../images/edit.svg"; import edit from "../../../images/edit.svg";
import accept from "../../../images/accept.png"; import accept from "../../../assets/images/accept.png";
import close from "../../../images/closeProjectPersons.svg"; import close from "../../../images/closeProjectPersons.svg";
import "./ticketFullScreen.scss"; import "./ticketFullScreen.scss";

View File

@ -2,10 +2,10 @@ import React, { useEffect, useRef, useState } from "react";
import { Link, useParams } from "react-router-dom"; import { Link, useParams } from "react-router-dom";
import { ProfileHeader } from "../../components/ProfileHeader/ProfileHeader"; import { ProfileHeader } from "../../components/ProfileHeader/ProfileHeader";
import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs"; import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs";
import { Footer } from "../../components/Footer/Footer"; import { Footer } from "@components/Common/Footer/Footer";
import { Navigation } from "../../components/Navigation/Navigation"; import { Navigation } from "../../components/Navigation/Navigation";
import { Loader } from "../../components/Loader/Loader"; import { Loader } from "@components/Common/Loader/Loader";
import { urlForLocal } from '../../helper' import { urlForLocal } from '../../utils/helper'
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { apiRequest } from "../../api/request"; import { apiRequest } from "../../api/request";
@ -26,18 +26,18 @@ import {
} from "../../redux/projectsTrackerSlice"; } from "../../redux/projectsTrackerSlice";
import ModalTicket from "../../components/UI/ModalTicket/ModalTicket"; import ModalTicket from "../../components/UI/ModalTicket/ModalTicket";
import TrackerModal from "../../components/UI/TrackerModal/TrackerModal"; import TrackerModal from "../../components/Modal/TrackerModal/TrackerModal";
import project from "../../images/trackerProject.svg"; import project from "../../assets/icons/trackerProject.svg";
import tasks from "../../images/trackerTasks.svg"; import tasks from "../../assets/icons/trackerTasks.svg";
import archive from "../../images/archiveTracker.svg"; import archive from "../../assets/icons/archiveTracker.svg";
import commentsBoard from "../../images/commentsBoard.svg"; import commentsBoard from "../../assets/icons/commentsBoard.svg";
import filesBoard from "../../images/filesBoard.svg"; import filesBoard from "../../assets/icons/filesBoard.svg";
import arrow from "../../images/arrowCalendar.png"; import arrow from "../../assets/icons/arrows/arrowCalendar.png";
import del from "../../images/delete.svg"; import del from "../../assets/icons/delete.svg";
import edit from "../../images/edit.svg"; import edit from "../../assets/icons/edit.svg";
import close from "../../images/closeProjectPersons.svg" import close from "../../assets/icons/close.png"
import accept from "../../images/accept.png"; import accept from "../../assets/images/accept.png";
export const ProjectTracker = () => { export const ProjectTracker = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();