Merge pull request #92 from apuc/tracker-connect-back

task add users
This commit is contained in:
NikoM1k 2023-05-24 01:21:40 +03:00 committed by GitHub
commit 5c47c78599
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 241 additions and 20 deletions

View File

@ -13,7 +13,6 @@ 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";
import taskImg from "../../../images/tasksMock.png";
import arrow from "../../../images/arrowStart.png"; import arrow from "../../../images/arrowStart.png";
import link from "../../../images/link.svg"; import link from "../../../images/link.svg";
import archive from "../../../images/archive.svg"; import archive from "../../../images/archive.svg";
@ -22,8 +21,10 @@ import edit from "../../../images/edit.svg";
import send from "../../../images/send.svg"; import send from "../../../images/send.svg";
import plus from "../../../images/plus.svg"; import plus from "../../../images/plus.svg";
import fullScreen from "../../../images/inFullScreen.svg"; import fullScreen from "../../../images/inFullScreen.svg";
import close from "../../../images/closeProjectPersons.svg";
import "./ModalTicket.scss"; import "./ModalTicket.scss";
import {urlForLocal} from "../../../helper";
export const ModalTiсket = ({ export const ModalTiсket = ({
active, active,
@ -31,6 +32,7 @@ export const ModalTiсket = ({
task, task,
projectId, projectId,
projectName, projectName,
projectUsers
}) => { }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [addSubtask, setAddSubtask] = useState(false); const [addSubtask, setAddSubtask] = useState(false);
@ -39,6 +41,11 @@ export const ModalTiсket = ({
const [comments, setComments] = useState([]); const [comments, setComments] = useState([]);
const [commentsEditOpen, setCommentsEditOpen] = useState({}) const [commentsEditOpen, setCommentsEditOpen] = useState({})
const [commentsEditText, setCommentsEditText] = useState({}) const [commentsEditText, setCommentsEditText] = useState({})
const [dropListOpen, setDropListOpen] = useState(false)
const [dropListMembersOpen, setDropListMembersOpen] = useState(false)
const [executor, setExecutor] = useState(task.executor)
const [members, setMembers] = useState(task.taskUsers)
const [users, setUsers] = useState([])
function deleteTask() { function deleteTask() {
apiRequest("/task/update-task", { apiRequest("/task/update-task", {
@ -107,6 +114,56 @@ export const ModalTiсket = ({
}) })
} }
function taskExecutor(person) {
apiRequest("/task/update-task", {
method: "PUT",
data: {
task_id: task.id,
executor_id: person.user_id
},
}).then((res) => {
setDropListOpen(false)
setExecutor(res.executor)
});
}
function deleteTaskExecutor() {
apiRequest("/task/update-task", {
method: "PUT",
data: {
task_id: task.id,
executor_id: null
},
}).then((res) => {
setExecutor(null)
});
}
function addMember(person) {
apiRequest("/task/add-user-to-task", {
method: "POST",
data: {
task_id: task.id,
user_id: person.user_id
},
}).then((res) => {
setDropListMembersOpen(false)
setMembers((prevValue) => ([...prevValue, res]))
});
}
function deleteMember(person) {
apiRequest("/task/del-user", {
method: "DELETE",
data: {
task_id: task.id,
user_id: person.user_id
},
}).then((res) => {
setMembers(members.filter((item) => item.user_id !== person.user_id))
});
}
useEffect(() => { useEffect(() => {
apiRequest(`/comment/get-by-entity?entity_type=2&entity_id=${task.id}`).then((res) => { apiRequest(`/comment/get-by-entity?entity_type=2&entity_id=${task.id}`).then((res) => {
setComments(res) setComments(res)
@ -117,6 +174,15 @@ export const ModalTiсket = ({
}) })
}, []) }, [])
useEffect(() => {
let ids = members.map((user) => user.user_id)
setUsers(projectUsers.reduce((acc, cur) => {
if (!ids.includes(cur.user_id)) acc.push(cur)
return acc
}, []))
}, [members])
return ( return (
<div <div
className={active ? "modal-tiket active" : "modal-tiket"} className={active ? "modal-tiket active" : "modal-tiket"}
@ -202,25 +268,66 @@ export const ModalTiсket = ({
</div> </div>
</div> </div>
<div className="workers"> <div className="workers">
<div className="workers_box"> <div className="workers_box task__info">
<span className="exit" onClick={() => setActive(false)}></span> <span className="exit" onClick={() => setActive(false)}></span>
<span>{task.title}</span> <span className='nameProject'>{task.title}</span>
<p className="workers__creator">Создатель : {task.user?.fio}</p> <p className="workers__creator">Создатель : {task.user?.fio}</p>
<div>
{Boolean(task.taskUsers?.length) && {executor ?
task.taskUsers.map((worker, index) => { <div className='executor'>
return ( <p>Исполнитель: {executor.fio}</p>
<div className="worker" key={index}> <img src={urlForLocal(executor.avatar)} alt='avatar' />
<img src={worker.avatar}></img> <img src={close} className='delete' onClick={() => deleteTaskExecutor()} />
<p>{worker.name}</p> </div> :
<div className="add-worker moreItems ">
<button onClick={() => setDropListOpen(true)}>+</button>
<span>Добавить исполнителя</span>
{dropListOpen &&
<div className='dropdownList'>
<img src={close} className='dropdownList__close' onClick={() => setDropListOpen(false)} />
{projectUsers.map((person) => {
return <div className='dropdownList__person' key={person.user_id} onClick={() => taskExecutor(person)}>
<span>{person.user.fio}</span>
<img src={urlForLocal(person.user.avatar)} />
</div> </div>
); })
})} }
</div> </div>
}
</div>
}
{Boolean(members.length) &&
<div className='members'>
<p>Участники:</p>
<div className='members__list'>
{members.map((member) => {
return <div className='worker' key={member.user_id}>
<p>{member.fio}</p>
<img src={urlForLocal(member.avatar)} />
<img src={close} className='delete' onClick={() => deleteMember(member)} />
</div>
})
}
</div>
</div>
}
<div className="add-worker moreItems"> <div className="add-worker moreItems">
<button>+</button> <button onClick={() => setDropListMembersOpen(true)}>+</button>
<span>Добавить участников</span> <span>Добавить участников</span>
{dropListMembersOpen &&
<div className='dropdownList'>
<img src={close} className='dropdownList__close' onClick={() => setDropListMembersOpen(false)} />
{users.length ? users.map((person) => {
return <div className='dropdownList__person' key={person.user_id} onClick={() => addMember(person)}>
<span>{person.user.fio}</span>
<img src={urlForLocal(person.user.avatar)} />
</div>
}) : <p className='noUsers'>Нет пользователей</p>
}
</div>
}
</div> </div>
</div> </div>
@ -228,7 +335,7 @@ export const ModalTiсket = ({
<div className="time"> <div className="time">
<img src={watch}></img> <img src={watch}></img>
<span>Длительность : </span> <span>Длительность : </span>
<p>{"8:30:22"}</p> <p>{"0:00:00"}</p>
</div> </div>
<button className="start"> <button className="start">

View File

@ -73,6 +73,10 @@
font-size: 16px; font-size: 16px;
line-height: 24px; line-height: 24px;
color: #1a1919; color: #1a1919;
max-width: 500px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin-bottom: 0; margin-bottom: 0;
} }
@ -224,6 +228,18 @@
} }
} }
.members {
display: flex;
flex-direction: column;
font-size: 14px;
&__list {
display: flex;
flex-direction: column;
row-gap: 8px;
}
}
.workers { .workers {
position: relative; position: relative;
border-left: 1px solid #f1f1f1; border-left: 1px solid #f1f1f1;
@ -258,9 +274,17 @@
color: #807777; color: #807777;
} }
.nameProject {
max-width: 200px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.add-worker { .add-worker {
display: flex; display: flex;
align-items: center; align-items: center;
position: relative;
span { span {
color: #000000; color: #000000;
@ -295,6 +319,8 @@
color: white; color: white;
background: #1458dd; background: #1458dd;
border-radius: 44px; border-radius: 44px;
opacity: 0.5;
pointer-events: none;
img { img {
margin-left: 10px; margin-left: 10px;
@ -320,7 +346,6 @@
line-height: 32px; line-height: 32px;
font-weight: 500; font-weight: 500;
color: #2d4a17; color: #2d4a17;
display: flex;
max-width: 200px; max-width: 200px;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
@ -339,12 +364,85 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
margin-bottom: 15px;
p { p {
margin: 0 0 0 11px; max-width: 170px;
font-size: 12px; overflow: hidden;
color: #807777; 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;
}
}
} }
} }

View File

@ -347,6 +347,17 @@ export const TicketFullScreen = ({}) => {
})} })}
</div> </div>
<div className="add-worker moreItems">
<button
onClick={() => {
dispatch(modalToggle("addWorker"));
setModalAddWorker(true);
}}
>
+
</button>
<span>Добавить исполнителя</span>
</div>
<div className="add-worker moreItems"> <div className="add-worker moreItems">
<button <button
onClick={() => { onClick={() => {
@ -364,7 +375,7 @@ export const TicketFullScreen = ({}) => {
<div className="time"> <div className="time">
<img src={watch}></img> <img src={watch}></img>
<span>Длительность : </span> <span>Длительность : </span>
<p>{"8:30:22"}</p> <p>{"0:00:00"}</p>
</div> </div>
<button className="start"> <button className="start">

View File

@ -297,6 +297,7 @@ export const ProjectTracker = () => {
task={selectedTicket} task={selectedTicket}
projectId={projectBoard.id} projectId={projectBoard.id}
projectName={projectBoard.name} projectName={projectBoard.name}
projectUsers={projectBoard.projectUsers}
/>} />}
<div className="tasks__container"> <div className="tasks__container">

View File

@ -555,6 +555,10 @@
font-weight: 400; font-weight: 400;
font-size: 14px; font-size: 14px;
line-height: 140%; line-height: 140%;
max-width: 295px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} }
&__info { &__info {