Merge pull request #91 from apuc/tracker-connect-back
Tracker connect back
This commit is contained in:
commit
5af1562917
@ -8,6 +8,8 @@ import {
|
|||||||
setProjectBoardFetch,
|
setProjectBoardFetch,
|
||||||
} from "../../../redux/projectsTrackerSlice";
|
} from "../../../redux/projectsTrackerSlice";
|
||||||
|
|
||||||
|
import {getCorrectDate} from '../../../components/Calendar/calendarHelper'
|
||||||
|
|
||||||
import category from "../../../images/category.png";
|
import category from "../../../images/category.png";
|
||||||
import watch from "../../../images/watch.png";
|
import watch from "../../../images/watch.png";
|
||||||
import file from "../../../images/fileModal.svg";
|
import file from "../../../images/fileModal.svg";
|
||||||
@ -33,7 +35,10 @@ export const ModalTiсket = ({
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [addSubtask, setAddSubtask] = useState(false);
|
const [addSubtask, setAddSubtask] = useState(false);
|
||||||
const [editOpen, setEditOpen] = useState(false);
|
const [editOpen, setEditOpen] = useState(false);
|
||||||
const [inputsValue, setInputsValue] = useState({title: task.title, description: task.description})
|
const [inputsValue, setInputsValue] = useState({title: task.title, description: task.description, comment: ''});
|
||||||
|
const [comments, setComments] = useState([]);
|
||||||
|
const [commentsEditOpen, setCommentsEditOpen] = useState({})
|
||||||
|
const [commentsEditText, setCommentsEditText] = useState({})
|
||||||
|
|
||||||
function deleteTask() {
|
function deleteTask() {
|
||||||
apiRequest("/task/update-task", {
|
apiRequest("/task/update-task", {
|
||||||
@ -61,6 +66,57 @@ export const ModalTiсket = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createComment() {
|
||||||
|
apiRequest("/comment/create", {
|
||||||
|
method: "POST",
|
||||||
|
data: {
|
||||||
|
text: inputsValue.comment,
|
||||||
|
entity_type: 2,
|
||||||
|
entity_id: task.id
|
||||||
|
}
|
||||||
|
}).then((res) => {
|
||||||
|
let newComment = res
|
||||||
|
newComment.created_at = new Date()
|
||||||
|
setInputsValue((prevValue) => ({...prevValue, comment: ''}))
|
||||||
|
setComments((prevValue) => ([...prevValue, newComment]))
|
||||||
|
setCommentsEditOpen((prevValue) => ({...prevValue, [res.id]: false}))
|
||||||
|
setCommentsEditText((prevValue) => ({...prevValue, [res.id]: res.text}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function deleteComment(commentId) {
|
||||||
|
apiRequest("/comment/update", {
|
||||||
|
method: "PUT",
|
||||||
|
data: {
|
||||||
|
comment_id: commentId,
|
||||||
|
status: 0
|
||||||
|
}
|
||||||
|
}).then((res) => {
|
||||||
|
setComments((prevValue) => prevValue.filter((item) => item.id !== commentId))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function editComment(commentId) {
|
||||||
|
|
||||||
|
apiRequest("/comment/update", {
|
||||||
|
method: "PUT",
|
||||||
|
data: {
|
||||||
|
comment_id: commentId,
|
||||||
|
text: commentsEditText[commentId]
|
||||||
|
}
|
||||||
|
}).then((res) => {
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
apiRequest(`/comment/get-by-entity?entity_type=2&entity_id=${task.id}`).then((res) => {
|
||||||
|
setComments(res)
|
||||||
|
res.forEach((item) => {
|
||||||
|
setCommentsEditOpen((prevValue) => ({...prevValue, [item.id]: false}))
|
||||||
|
setCommentsEditText((prevValue) => ({...prevValue, [item.id]: item.text}))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={active ? "modal-tiket active" : "modal-tiket"}
|
className={active ? "modal-tiket active" : "modal-tiket"}
|
||||||
@ -91,7 +147,7 @@ export const ModalTiсket = ({
|
|||||||
{editOpen ? <input value={inputsValue.description} onChange={(e) => {
|
{editOpen ? <input value={inputsValue.description} onChange={(e) => {
|
||||||
setInputsValue((prevValue) => ({...prevValue, description: e.target.value}))
|
setInputsValue((prevValue) => ({...prevValue, description: e.target.value}))
|
||||||
}}/> :<p>{inputsValue.description}</p>}
|
}}/> :<p>{inputsValue.description}</p>}
|
||||||
<img src={taskImg} className="image-task"></img>
|
{/*<img src={taskImg} className="image-task"></img>*/}
|
||||||
</div>
|
</div>
|
||||||
<div className="content__communication">
|
<div className="content__communication">
|
||||||
<p className="tasks">
|
<p className="tasks">
|
||||||
@ -115,8 +171,33 @@ export const ModalTiсket = ({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="content__input">
|
<div className="content__input">
|
||||||
<input placeholder="Оставить комментарий"></input>
|
<input placeholder="Оставить комментарий" value={inputsValue.comment} onChange={(e) => {
|
||||||
<img src={send}></img>
|
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>
|
||||||
|
<img src={del} alt='delete' onClick={() => deleteComment(comment.id)} />
|
||||||
|
</div>
|
||||||
|
{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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -75,6 +75,46 @@
|
|||||||
color: #1a1919;
|
color: #1a1919;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comments__list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
row-gap: 10px;
|
||||||
|
&__item {
|
||||||
|
padding: 10px 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
max-width: 438px;
|
||||||
|
background: #f1f1f1;
|
||||||
|
border-radius: 44px;
|
||||||
|
font-size: 14px;
|
||||||
|
width: 100%;
|
||||||
|
row-gap: 10px;
|
||||||
|
|
||||||
|
&__info {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
column-gap: 15px;
|
||||||
|
|
||||||
|
.edit {
|
||||||
|
width: 25px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit__open {
|
||||||
|
background: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__description {
|
&__description {
|
||||||
|
@ -8,8 +8,15 @@ import TrackerModal from "../TrackerModal/TrackerModal";
|
|||||||
import { Navigation } from "../../Navigation/Navigation";
|
import { Navigation } from "../../Navigation/Navigation";
|
||||||
import {Loader} from "../../Loader/Loader";
|
import {Loader} from "../../Loader/Loader";
|
||||||
|
|
||||||
import { useDispatch } from "react-redux";
|
import {useDispatch, useSelector} from "react-redux";
|
||||||
import {modalToggle, setToggleTab} from "../../../redux/projectsTrackerSlice";
|
import {
|
||||||
|
deletePersonOnProject,
|
||||||
|
modalToggle,
|
||||||
|
setProjectBoardFetch,
|
||||||
|
setToggleTab,
|
||||||
|
getProjectBoard,
|
||||||
|
getBoarderLoader
|
||||||
|
} from "../../../redux/projectsTrackerSlice";
|
||||||
import { apiRequest } from "../../../api/request";
|
import { apiRequest } from "../../../api/request";
|
||||||
|
|
||||||
import project from "../../../images/trackerProject.svg";
|
import project from "../../../images/trackerProject.svg";
|
||||||
@ -22,7 +29,6 @@ import plus from "../../../images/plus.svg";
|
|||||||
import tasks from "../../../images/trackerTasks.svg";
|
import tasks from "../../../images/trackerTasks.svg";
|
||||||
import archive from "../../../images/archiveTracker.svg";
|
import archive from "../../../images/archiveTracker.svg";
|
||||||
import selectArrow from "../../../images/select.svg";
|
import selectArrow from "../../../images/select.svg";
|
||||||
import avatarTest from "../../../images/AvatarTest .png";
|
|
||||||
import arrow from "../../../images/arrowCalendar.png";
|
import arrow from "../../../images/arrowCalendar.png";
|
||||||
import link from "../../../images/link.svg";
|
import link from "../../../images/link.svg";
|
||||||
import archive2 from "../../../images/archive.svg";
|
import archive2 from "../../../images/archive.svg";
|
||||||
@ -30,28 +36,39 @@ import del from "../../../images/delete.svg";
|
|||||||
import edit from "../../../images/edit.svg";
|
import edit from "../../../images/edit.svg";
|
||||||
|
|
||||||
import "./ticketFullScreen.scss";
|
import "./ticketFullScreen.scss";
|
||||||
|
import close from "../../../images/closeProjectPersons.svg";
|
||||||
|
import {urlForLocal} from "../../../helper";
|
||||||
|
import {getCorrectDate} from "../../Calendar/calendarHelper";
|
||||||
|
|
||||||
export const TicketFullScreen = ({}) => {
|
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();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [projectInfo, setProjectInfo] = useState({});
|
const projectBoard = useSelector(getProjectBoard);
|
||||||
|
const boardLoader = useSelector(getBoarderLoader);
|
||||||
const [taskInfo, setTaskInfo] = useState({});
|
const [taskInfo, setTaskInfo] = useState({});
|
||||||
const [editOpen, setEditOpen] = useState(false);
|
const [editOpen, setEditOpen] = useState(false);
|
||||||
const [inputsValue, setInputsValue] = useState({})
|
const [inputsValue, setInputsValue] = useState({});
|
||||||
const [loader, setLoader] = useState(true)
|
const [loader, setLoader] = useState(true);
|
||||||
|
const [comments, setComments] = useState([]);
|
||||||
|
const [commentsEditOpen, setCommentsEditOpen] = useState({})
|
||||||
|
const [commentsEditText, setCommentsEditText] = useState({})
|
||||||
|
const [personListOpen, setPersonListOpen] = useState(false)
|
||||||
|
|
||||||
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({title: taskInfo.title, description: taskInfo.description})
|
setInputsValue({title: taskInfo.title, description: taskInfo.description, comment: ''})
|
||||||
apiRequest(`/project/get-project?project_id=${taskInfo.project_id}`).then(
|
apiRequest(`/comment/get-by-entity?entity_type=2&entity_id=${taskInfo.id}`).then((res) => {
|
||||||
(project) => {
|
setComments(res)
|
||||||
setProjectInfo(project);
|
res.forEach((item) => {
|
||||||
setLoader(false)
|
setCommentsEditOpen((prevValue) => ({...prevValue, [item.id]: false}))
|
||||||
}
|
setCommentsEditText((prevValue) => ({...prevValue, [item.id]: item.text}))
|
||||||
);
|
})
|
||||||
|
})
|
||||||
|
dispatch(setProjectBoardFetch(taskInfo.project_id));
|
||||||
|
setLoader(boardLoader)
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -79,6 +96,59 @@ export const TicketFullScreen = ({}) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createComment() {
|
||||||
|
apiRequest("/comment/create", {
|
||||||
|
method: "POST",
|
||||||
|
data: {
|
||||||
|
text: inputsValue.comment,
|
||||||
|
entity_type: 2,
|
||||||
|
entity_id: taskInfo.id
|
||||||
|
}
|
||||||
|
}).then((res) => {
|
||||||
|
let newComment = res
|
||||||
|
newComment.created_at = new Date()
|
||||||
|
setInputsValue((prevValue) => ({...prevValue, comment: ''}))
|
||||||
|
setComments((prevValue) => ([...prevValue, newComment]))
|
||||||
|
setCommentsEditOpen((prevValue) => ({...prevValue, [res.id]: false}))
|
||||||
|
setCommentsEditText((prevValue) => ({...prevValue, [res.id]: res.text}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteComment(commentId) {
|
||||||
|
apiRequest("/comment/update", {
|
||||||
|
method: "PUT",
|
||||||
|
data: {
|
||||||
|
comment_id: commentId,
|
||||||
|
status: 0
|
||||||
|
}
|
||||||
|
}).then((res) => {
|
||||||
|
setComments((prevValue) => prevValue.filter((item) => item.id !== commentId))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function editComment(commentId) {
|
||||||
|
apiRequest("/comment/update", {
|
||||||
|
method: "PUT",
|
||||||
|
data: {
|
||||||
|
comment_id: commentId,
|
||||||
|
text: commentsEditText[commentId]
|
||||||
|
}
|
||||||
|
}).then((res) => {
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function deletePerson(userId) {
|
||||||
|
apiRequest("/project/del-user", {
|
||||||
|
method: "DELETE",
|
||||||
|
data: {
|
||||||
|
project_id: projectBoard.id,
|
||||||
|
user_id: userId
|
||||||
|
},
|
||||||
|
}).then((res) => {
|
||||||
|
dispatch(deletePersonOnProject(userId))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const toggleTabs = (index) => {
|
const toggleTabs = (index) => {
|
||||||
dispatch(setToggleTab(index));
|
dispatch(setToggleTab(index));
|
||||||
};
|
};
|
||||||
@ -130,7 +200,7 @@ export const TicketFullScreen = ({}) => {
|
|||||||
<div className="tracker__tabs__content content-tabs">
|
<div className="tracker__tabs__content content-tabs">
|
||||||
<div className="tasks__head">
|
<div className="tasks__head">
|
||||||
<div className="tasks__head__wrapper">
|
<div className="tasks__head__wrapper">
|
||||||
<h4>Проект : {projectInfo.name}</h4>
|
<h4>Проект : {projectBoard.name}</h4>
|
||||||
|
|
||||||
<TrackerModal
|
<TrackerModal
|
||||||
active={modalAddWorker}
|
active={modalAddWorker}
|
||||||
@ -138,19 +208,45 @@ export const TicketFullScreen = ({}) => {
|
|||||||
></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">+9</span>
|
<span className="countPersons">{projectBoard.projectUsers?.length}</span>
|
||||||
<span
|
<span
|
||||||
className="addPerson"
|
className="addPerson"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch(modalToggle("addWorker"));
|
setPersonListOpen(true)
|
||||||
setModalAddWorker(true);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
+
|
+
|
||||||
</span>
|
</span>
|
||||||
<p>добавить участника в проект</p>
|
<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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className="tasks__head__select">
|
<div className="tasks__head__select">
|
||||||
<span>Учавствую</span>
|
<span>Учавствую</span>
|
||||||
@ -180,7 +276,7 @@ export const TicketFullScreen = ({}) => {
|
|||||||
{editOpen ? <input value={inputsValue.description} onChange={(e) => {
|
{editOpen ? <input value={inputsValue.description} onChange={(e) => {
|
||||||
setInputsValue((prevValue) => ({...prevValue, description: e.target.value}))
|
setInputsValue((prevValue) => ({...prevValue, description: e.target.value}))
|
||||||
}}/> :<p>{inputsValue.description}</p>}
|
}}/> :<p>{inputsValue.description}</p>}
|
||||||
<img src={task} className="image-task"></img>
|
{/*<img src={task} className="image-task"></img>*/}
|
||||||
</div>
|
</div>
|
||||||
<div className="content__communication">
|
<div className="content__communication">
|
||||||
<p className="tasks">
|
<p className="tasks">
|
||||||
@ -204,8 +300,33 @@ export const TicketFullScreen = ({}) => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="content__input">
|
<div className="content__input">
|
||||||
<input placeholder="Оставить комментарий"></input>
|
<input placeholder="Оставить комментарий" value={inputsValue.comment} onChange={(e) => {
|
||||||
<img src={send}></img>
|
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>
|
||||||
|
<img src={del} alt='delete' onClick={() => deleteComment(comment.id)} />
|
||||||
|
</div>
|
||||||
|
{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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,7 +12,8 @@ import {
|
|||||||
editProjectName,
|
editProjectName,
|
||||||
editColumnName,
|
editColumnName,
|
||||||
getColumnName,
|
getColumnName,
|
||||||
getColumnId
|
getColumnId,
|
||||||
|
addPersonToProject
|
||||||
} from "../../../redux/projectsTrackerSlice";
|
} from "../../../redux/projectsTrackerSlice";
|
||||||
|
|
||||||
import arrowDown from "../../../images/selectArrow.png"
|
import arrowDown from "../../../images/selectArrow.png"
|
||||||
@ -136,23 +137,36 @@ export const TrackerModal = ({
|
|||||||
apiRequest("/project/add-user", {
|
apiRequest("/project/add-user", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
data: {
|
data: {
|
||||||
user_id: selectedWorker.id,
|
user_id: selectedWorker.user_id,
|
||||||
project_id: projectBoard.id
|
project_id: projectBoard.id
|
||||||
}
|
}
|
||||||
}).then((el) => {
|
}).then((el) => {
|
||||||
|
dispatch(addPersonToProject(el))
|
||||||
setActive(false);
|
setActive(false);
|
||||||
selectedWorker(null)
|
setSelectedWorker('')
|
||||||
|
setSelectWorkersOpen(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
modalType === "addWorker" ? apiRequest('/project/my-employee').then((el) => setWorkers(el.managerEmployees)) : ''
|
modalType === "addWorker" ? apiRequest('/project/my-employee').then((el) => {
|
||||||
}, [modalType])
|
let persons = el.managerEmployees
|
||||||
|
let ids = projectBoard.projectUsers.map((user) => user.user_id)
|
||||||
|
setWorkers(persons.reduce((acc, cur) => {
|
||||||
|
if (!ids.includes(cur.user_id)) acc.push(cur)
|
||||||
|
return acc
|
||||||
|
}, []))
|
||||||
|
}) : ''
|
||||||
|
}, [active])
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={active ? "modal-add active" : "modal-add"}
|
className={active ? "modal-add active" : "modal-add"}
|
||||||
onClick={() => setActive(false)}
|
onClick={() => {
|
||||||
|
setActive(false)
|
||||||
|
setSelectWorkersOpen(false)
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className="modal-add__content" onClick={(e) => e.stopPropagation()}>
|
<div className="modal-add__content" onClick={(e) => e.stopPropagation()}>
|
||||||
{modalType === "addWorker" && (
|
{modalType === "addWorker" && (
|
||||||
@ -171,15 +185,21 @@ export const TrackerModal = ({
|
|||||||
<img className='arrow' src={arrowDown} alt='arrow' />
|
<img className='arrow' src={arrowDown} alt='arrow' />
|
||||||
{Boolean(selectWorkersOpen) &&
|
{Boolean(selectWorkersOpen) &&
|
||||||
<div className='select__worker__dropDown'>
|
<div className='select__worker__dropDown'>
|
||||||
{workers.map((worker) => {
|
{Boolean(workers.length) ?
|
||||||
|
workers.map((worker) => {
|
||||||
if (worker === selectedWorker) {
|
if (worker === selectedWorker) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return <div className='worker' key={worker.id} onClick={() => setSelectedWorker(worker)}>
|
return <div className='worker' key={worker.id} onClick={() =>
|
||||||
|
{
|
||||||
|
setSelectedWorker(worker)
|
||||||
|
}
|
||||||
|
}>
|
||||||
<p>{worker.employee.fio}</p>
|
<p>{worker.employee.fio}</p>
|
||||||
<img src={urlForLocal(worker.employee.avatar)} alt='avatar'/>
|
<img src={urlForLocal(worker.employee.avatar)} alt='avatar'/>
|
||||||
</div>
|
</div>
|
||||||
})
|
}) :
|
||||||
|
<div>Нет пользователей</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
3
src/images/closeProjectPersons.svg
Normal file
3
src/images/closeProjectPersons.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="9" height="8" viewBox="0 0 9 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M0.912203 -5.93063e-05C1.078 -0.00256001 1.17953 0.0699604 1.2738 0.164737C2.15005 1.04323 3.0278 1.92023 3.90504 2.79773C3.9859 2.87858 4.06642 2.87867 4.14661 2.79798C5.0061 1.93824 5.8656 1.07849 6.72534 0.219002C6.95365 -0.00906182 7.16121 -0.00781147 7.39403 0.221003C7.54257 0.367044 7.69161 0.512585 7.83915 0.659376C8.05321 0.872686 8.05446 1.0945 7.83915 1.31031C6.96516 2.18556 6.09116 3.0603 5.21416 3.9323C5.14239 4.00382 5.13689 4.03958 5.21266 4.11485C6.08516 4.97934 6.9534 5.84833 7.8229 6.71583C8.06171 6.95415 8.06096 7.1607 7.82115 7.40077C7.67385 7.54806 7.52756 7.6961 7.37952 7.84265C7.16821 8.05145 6.94815 8.05145 6.73784 7.8414C5.86284 6.9674 4.98735 6.09415 4.1161 5.21641C4.04008 5.13989 4.00582 5.14739 3.93455 5.21941C3.07131 6.08765 2.20457 6.9524 1.33832 7.81789C1.09625 8.05971 0.893197 8.06021 0.65338 7.82089C0.51184 7.6796 0.3703 7.53831 0.22926 7.39677C-0.00555557 7.16145 -0.00705598 6.9529 0.225259 6.71933C1.0915 5.84909 1.95725 4.97784 2.82674 4.1106C2.89976 4.03783 2.90326 4.00232 2.82924 3.92855C1.94299 3.04805 1.06074 2.1638 0.177496 1.2803C-0.0575702 1.04523 -0.0590706 0.835926 0.172495 0.604111C0.328538 0.447817 0.485583 0.292773 0.640876 0.135979C0.723149 0.0524555 0.820177 0.00394182 0.912203 -5.93063e-05Z" fill="#263238"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
@ -5,6 +5,7 @@ import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileB
|
|||||||
import { Footer } from "../../components/Footer/Footer";
|
import { Footer } from "../../components/Footer/Footer";
|
||||||
import { Navigation } from "../../components/Navigation/Navigation";
|
import { Navigation } from "../../components/Navigation/Navigation";
|
||||||
import { Loader } from "../../components/Loader/Loader";
|
import { Loader } from "../../components/Loader/Loader";
|
||||||
|
import { urlForLocal } from '../../helper'
|
||||||
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { apiRequest } from "../../api/request";
|
import { apiRequest } from "../../api/request";
|
||||||
@ -18,6 +19,7 @@ import {
|
|||||||
activeLoader,
|
activeLoader,
|
||||||
setColumnName,
|
setColumnName,
|
||||||
setColumnId,
|
setColumnId,
|
||||||
|
deletePersonOnProject
|
||||||
} from "../../redux/projectsTrackerSlice";
|
} from "../../redux/projectsTrackerSlice";
|
||||||
|
|
||||||
import ModalTicket from "../../components/UI/ModalTicket/ModalTicket";
|
import ModalTicket from "../../components/UI/ModalTicket/ModalTicket";
|
||||||
@ -32,6 +34,7 @@ import filesBoard from "../../images/filesBoard.svg";
|
|||||||
import arrow from "../../images/arrowCalendar.png";
|
import arrow from "../../images/arrowCalendar.png";
|
||||||
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 close from "../../images/closeProjectPersons.svg"
|
||||||
|
|
||||||
export const ProjectTracker = () => {
|
export const ProjectTracker = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@ -44,6 +47,7 @@ export const ProjectTracker = () => {
|
|||||||
const [modalAdd, setModalAdd] = useState(false);
|
const [modalAdd, setModalAdd] = useState(false);
|
||||||
const [modalActiveTicket, setModalActiveTicket] = useState(false);
|
const [modalActiveTicket, setModalActiveTicket] = useState(false);
|
||||||
const [selectedTicket, setSelectedTicket] = useState({});
|
const [selectedTicket, setSelectedTicket] = useState({});
|
||||||
|
const [personListOpen, setPersonListOpen] = useState(false)
|
||||||
|
|
||||||
const startWrapperIndexTest = useRef({});
|
const startWrapperIndexTest = useRef({});
|
||||||
const projectBoard = useSelector(getProjectBoard);
|
const projectBoard = useSelector(getProjectBoard);
|
||||||
@ -151,6 +155,18 @@ export const ProjectTracker = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deletePerson(userId) {
|
||||||
|
apiRequest("/project/del-user", {
|
||||||
|
method: "DELETE",
|
||||||
|
data: {
|
||||||
|
project_id: projectBoard.id,
|
||||||
|
user_id: userId
|
||||||
|
},
|
||||||
|
}).then((res) => {
|
||||||
|
dispatch(deletePersonOnProject(userId))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tracker">
|
<div className="tracker">
|
||||||
<ProfileHeader />
|
<ProfileHeader />
|
||||||
@ -226,13 +242,39 @@ export const ProjectTracker = () => {
|
|||||||
<span
|
<span
|
||||||
className="addPerson"
|
className="addPerson"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch(modalToggle("addWorker"));
|
setPersonListOpen(true)
|
||||||
setModalAdd(true);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
+
|
+
|
||||||
</span>
|
</span>
|
||||||
<p>добавить участника</p>
|
<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"));
|
||||||
|
setModalAdd(true);
|
||||||
|
setPersonListOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className='addPerson'>+</span>
|
||||||
|
<p>Добавить участников</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className="tasks__head__select">
|
<div className="tasks__head__select">
|
||||||
<span>Участвую</span>
|
<span>Участвую</span>
|
||||||
|
@ -309,6 +309,118 @@
|
|||||||
line-height: 17px;
|
line-height: 17px;
|
||||||
max-width: 125px;
|
max-width: 125px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.persons__list {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: linear-gradient(180deg, #FFFFFF 0%, #EBEBEB 100%);
|
||||||
|
border-radius: 40px;
|
||||||
|
padding: 33px 24px 44px 34px;
|
||||||
|
width: 425px;
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
&__close {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__count {
|
||||||
|
display: flex;
|
||||||
|
align-items: end;
|
||||||
|
color: #1458DD;
|
||||||
|
font-size: 22px;
|
||||||
|
margin-top: 10px;
|
||||||
|
span {
|
||||||
|
font-size: 44px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 40px;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__info {
|
||||||
|
display: flex;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 22px;
|
||||||
|
color: #263238;
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 13px 0 32px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
color: #1458DD;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__items {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
row-gap: 60px;
|
||||||
|
column-gap: 35px;
|
||||||
|
margin-bottom: 38px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
width: 145px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: initial;
|
||||||
|
color: #807777;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
max-width: 85px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__add {
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
span {
|
||||||
|
background: #8BCC60;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-left: 17px;
|
||||||
|
color: #000000;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 32px;
|
||||||
|
position: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__select {
|
&__select {
|
||||||
|
@ -35,6 +35,12 @@ export const projectsTrackerSlice = createSlice({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
deletePersonOnProject: (state,action) => {
|
||||||
|
state.projectBoard.projectUsers = state.projectBoard.projectUsers.filter((person) => person.user_id !== action.payload)
|
||||||
|
},
|
||||||
|
addPersonToProject: (state, action) => {
|
||||||
|
state.projectBoard.projectUsers.push(action.payload)
|
||||||
|
},
|
||||||
activeLoader: (state) => {
|
activeLoader: (state) => {
|
||||||
state.boardLoader = true;
|
state.boardLoader = true;
|
||||||
},
|
},
|
||||||
@ -100,7 +106,9 @@ export const {
|
|||||||
activeLoader,
|
activeLoader,
|
||||||
editProjectName,
|
editProjectName,
|
||||||
editColumnName,
|
editColumnName,
|
||||||
setColumnId
|
setColumnId,
|
||||||
|
deletePersonOnProject,
|
||||||
|
addPersonToProject
|
||||||
} = projectsTrackerSlice.actions;
|
} = projectsTrackerSlice.actions;
|
||||||
|
|
||||||
export const getProjects = (state) => state.tracker.projects;
|
export const getProjects = (state) => state.tracker.projects;
|
||||||
|
Loading…
Reference in New Issue
Block a user