This commit is contained in:
Mikola 2023-10-25 16:33:08 +03:00
parent 33b70c7fbd
commit fc9d18a380
5 changed files with 320 additions and 12 deletions

View File

@ -39,6 +39,7 @@ import file from "assets/icons/fileModal.svg";
import link from "assets/icons/link.svg"; import link from "assets/icons/link.svg";
import send from "assets/icons/send.svg"; import send from "assets/icons/send.svg";
import watch from "assets/icons/watch.svg"; import watch from "assets/icons/watch.svg";
import arrowDown from "assets/icons/arrows/selectArrow.png";
import avatarMok from "assets/images/avatarMok.png"; import avatarMok from "assets/images/avatarMok.png";
import { getCorrectDate } from "../../../Calendar/calendarHelper"; import { getCorrectDate } from "../../../Calendar/calendarHelper";
@ -54,6 +55,7 @@ export const ModalTiсket = ({
projectName, projectName,
projectUsers, projectUsers,
projectOwnerId, projectOwnerId,
projectMarks
}) => { }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [addSubtask, setAddSubtask] = useState(false); const [addSubtask, setAddSubtask] = useState(false);
@ -73,6 +75,7 @@ export const ModalTiсket = ({
const [dropListMembersOpen, setDropListMembersOpen] = useState(false); const [dropListMembersOpen, setDropListMembersOpen] = useState(false);
const [executor, setExecutor] = useState(task.executor); const [executor, setExecutor] = useState(task.executor);
const [members, setMembers] = useState(task.taskUsers); const [members, setMembers] = useState(task.taskUsers);
const [taskTags, setTaskTags] = useState(task.mark)
const [users, setUsers] = useState([]); const [users, setUsers] = useState([]);
const [timerStart, setTimerStart] = useState(false); const [timerStart, setTimerStart] = useState(false);
const [timerInfo, setTimerInfo] = useState({}); const [timerInfo, setTimerInfo] = useState({});
@ -85,9 +88,11 @@ export const ModalTiсket = ({
const [timerId, setTimerId] = useState(null); const [timerId, setTimerId] = useState(null);
const [taskFiles, setTaskFiles] = useState([]); const [taskFiles, setTaskFiles] = useState([]);
const [correctProjectUsers, setCorrectProjectUsers] = useState(projectUsers); const [correctProjectUsers, setCorrectProjectUsers] = useState(projectUsers);
const [correctProjectTags, setCorrectProjectTags] = useState([])
const [executorId, setExecutorId] = useState(task.executor_id); const [executorId, setExecutorId] = useState(task.executor_id);
const profileInfo = useSelector(getProfileInfo); const profileInfo = useSelector(getProfileInfo);
const [acceptModalOpen, setAcceptModalOpen] = useState(false); const [acceptModalOpen, setAcceptModalOpen] = useState(false);
const [selectTagsOpen, setSelectTagsOpen] = useState(false)
const { showNotification } = useNotification(); const { showNotification } = useNotification();
function deleteTask() { function deleteTask() {
@ -347,6 +352,14 @@ export const ModalTiсket = ({
} }
}, []); }, []);
useEffect(() => {
let tagIds = taskTags.map((tag) => tag.id)
setCorrectProjectTags(projectMarks.reduce((acc, cur) => {
if (!tagIds.includes(cur.id)) acc.push(cur)
return acc
}, []))
}, [taskTags])
async function handleUpload(event) { async function handleUpload(event) {
const formData = new FormData(); const formData = new FormData();
formData.append("uploadFile", event.target.files[0]); formData.append("uploadFile", event.target.files[0]);
@ -454,6 +467,35 @@ export const ModalTiсket = ({
}); });
} }
function addTagToTask(tagId) {
apiRequest("/mark/attach", {
method: "POST",
data: {
mark_id: tagId,
entity_type: 2,
entity_id: task.id
}
}).then((data) => {
setSelectTagsOpen(false)
setTaskTags((prevValue) => [...prevValue, data.mark])
dispatch(setProjectBoardFetch(projectId));
})
}
function deleteTagFromTask(tagId) {
apiRequest("/mark/detach", {
method: "DELETE",
data: {
mark_id: tagId,
entity_type: 2,
entity_id: task.id
}
}).then(() => {
setTaskTags((prevValue) => prevValue.filter((tag) => tag.id !== tagId))
dispatch(setProjectBoardFetch(projectId));
})
}
function closeAcceptModal() { function closeAcceptModal() {
setAcceptModalOpen(false); setAcceptModalOpen(false);
} }
@ -478,7 +520,6 @@ export const ModalTiсket = ({
<img src={fullScreen}></img> <img src={fullScreen}></img>
</Link> </Link>
</h3> </h3>
<div className="content__task"> <div className="content__task">
{editOpen ? ( {editOpen ? (
<input <input
@ -813,6 +854,42 @@ export const ModalTiсket = ({
</div> </div>
<div className="workers_box-bottom"> <div className="workers_box-bottom">
<div className='tags'>
<div className='tags__selected'>
{taskTags.map((tag) => {
return <div className='tags__selected__item' key={tag.id} style={{background: tag.color}}>
<p>
{tag.slug}
</p>
<img src={close} className='delete' alt='delete' onClick={() => deleteTagFromTask(tag.id)} />
</div>
})
}
</div>
<div className='tags__select' onClick={() => setSelectTagsOpen(!selectTagsOpen)}>
<span>Выберите тег</span>
<img
className={selectTagsOpen ? "open" : ""}
src={arrowDown}
alt="arrow"
/>
</div>
{selectTagsOpen &&
<div className='tags__dropDown'>
<img onClick={() => setSelectTagsOpen(false)} className='tags__dropDown__close' src={close} alt="close" />
{correctProjectTags.map((tag) => {
return <div className='tagItem' key={tag.id} onClick={() => addTagToTask(tag.id)}>
<p>{tag.slug}</p>
<span style={{background: tag.color}} />
</div>
})
}
{!Boolean(correctProjectTags.length) &&
<p className='tags__dropDown__noItem'>Нет тегов</p>
}
</div>
}
</div>
<div <div
className={editOpen ? "edit" : ""} className={editOpen ? "edit" : ""}
onClick={() => { onClick={() => {

View File

@ -971,7 +971,7 @@
} }
&-bottom { &-bottom {
padding: 20px 110px 35px 56px; padding: 10px 110px 15px 56px;
font-weight: 400; font-weight: 400;
font-size: 14px; font-size: 14px;
line-height: 38px; line-height: 38px;
@ -991,6 +991,129 @@
} }
} }
.tags {
display: flex;
flex-direction: column;
position: relative;
row-gap: 5px;
padding: 5px;
border-radius: 10px;
border: 1px solid #e0e0e0;
margin-bottom: 10px;
&__selected {
display: flex;
flex-wrap: wrap;
padding: 0;
width: 100%;
gap: 8px;
max-width: 200px;
&__item {
display: flex;
padding: 3px 5px 3px 8px;
border-radius: 8px;
align-items: center;
column-gap: 8px;
cursor: default;
p {
font-weight: 600;
text-decoration: none !important;
font-size: 15px;
margin: 0;
line-height: 15px;
color: white;
}
.delete {
cursor: pointer;
width: 12px;
height: 12px;
}
}
}
&__select {
width: 100%;
height: 25px;
border-radius: 8px;
border: 1px solid gray;
padding: 5px 8px;
display: flex;
justify-content: space-between;
span {
font-size: 14px;
color: black;
}
img {
transition: all 0.3s ease;
}
.open {
transform: rotate(180deg);
}
}
&__dropDown {
position: absolute;
padding: 15px 10px 10px;
background: white;
width: 100%;
border-radius: 8px;
border: 1px solid #e4e4e4;
top: 110%;
display: flex;
flex-direction: column;
row-gap: 4px;
z-index: 10;
&__close {
width: 8px;
height: 8px;
position: absolute;
right: 8px;
top: 5px;
}
.tagItem {
display: flex;
width: 100%;
cursor: pointer;
column-gap: 8px;
padding: 5px;
border: 1px solid #ececec;
border-radius: 8px;
justify-content: space-between;
p {
font-size: 18px;
font-weight: 500;
margin: 0;
line-height: 20px;
text-decoration: none;
}
span {
width: 18px;
height: 18px;
border-radius: 50px;
}
}
&__noItem {
line-height: 20px;
font-size: 15px;
margin: 0;
font-weight: 500;
text-decoration: none !important;
cursor: default;
}
}
}
.edit { .edit {
background: #52b709; background: #52b709;
border-radius: 50px; border-radius: 50px;

View File

@ -12,6 +12,7 @@ import {
filteredParticipateTasks, filteredParticipateTasks,
getBoarderLoader, getBoarderLoader,
getProjectBoard, getProjectBoard,
deleteTagProject,
modalToggle, modalToggle,
movePositionProjectTask, movePositionProjectTask,
moveProjectTask, moveProjectTask,
@ -71,6 +72,7 @@ export const ProjectTracker = () => {
const [tags, setTags] = useState({ const [tags, setTags] = useState({
open: false, open: false,
add: false, add: false,
edit: false
}); });
const [color, setColor] = useState("#aabbcc"); const [color, setColor] = useState("#aabbcc");
const [tagInfo, setTagInfo] = useState({ description: "", name: "" }); const [tagInfo, setTagInfo] = useState({ description: "", name: "" });
@ -318,6 +320,37 @@ export const ProjectTracker = () => {
}); });
} }
function editTag() {
apiRequest("/mark/update", {
method: "PUT",
data: {
mark_id: tagInfo.editMarkId,
title: tagInfo.description,
slug: tagInfo.name,
color: color
}
}).then(() => {
dispatch(setProjectBoardFetch(projectId.id))
setTags((prevState) => ({
...prevState,
edit: false,
}));
})
}
function deleteTag(tagId) {
apiRequest("/mark/detach", {
method: "DELETE",
data: {
mark_id: tagId,
entity_type: 1,
entity_id: projectId.id,
}
}).then(() => {
dispatch(deleteTagProject(tagId))
})
}
return ( return (
<div className="tracker"> <div className="tracker">
<ProfileHeader /> <ProfileHeader />
@ -586,13 +619,19 @@ export const ProjectTracker = () => {
className="close" className="close"
alt="close" alt="close"
onClick={() => { onClick={() => {
setTags((prevState) => ({ setTags({
...prevState,
open: false, open: false,
})); add: false,
edit: false
});
setTagInfo({
description: '',
name: ''
})
setColor("#aabbcc")
}} }}
/> />
{!tags.add && ( {!tags.add && !tags.edit && (
<div className="tags__list__created"> <div className="tags__list__created">
{projectBoard.mark.map((tag) => { {projectBoard.mark.map((tag) => {
return ( return (
@ -609,6 +648,21 @@ export const ProjectTracker = () => {
style={{ background: tag.color }} style={{ background: tag.color }}
/> />
</div> </div>
<div className='tagItem__images'>
<img src={edit} alt='edit' onClick={() => {
setTags((prevState) => ({
...prevState,
edit: true,
}))
setTagInfo({
description: tag.title,
name: tag.slug,
editMarkId: tag.id
})
setColor(tag.color)
}} />
<img onClick={() => deleteTag(tag.id)} className='delete' src={close} alt='delete' />
</div>
</div> </div>
); );
})} })}
@ -626,7 +680,7 @@ export const ProjectTracker = () => {
</div> </div>
</div> </div>
)} )}
{tags.add && ( {(tags.add || tags.edit) && (
<div className="formTag"> <div className="formTag">
<img <img
src={arrow} src={arrow}
@ -636,7 +690,13 @@ export const ProjectTracker = () => {
setTags((prevState) => ({ setTags((prevState) => ({
...prevState, ...prevState,
add: false, add: false,
edit: false
})); }));
setTagInfo({
description: '',
name: ''
})
setColor("#aabbcc")
}} }}
/> />
<input <input
@ -665,14 +725,17 @@ export const ProjectTracker = () => {
/> />
<HexColorPicker color={color} onChange={setColor} /> <HexColorPicker color={color} onChange={setColor} />
<button <button
onClick={addNewTag} onClick={() => {
tags.add ? addNewTag() : editTag()
}
}
className={ className={
tagInfo.name && tagInfo.description tagInfo.name && tagInfo.description
? "formTag__btn" ? "formTag__btn"
: "formTag__btn disable" : "formTag__btn disable"
} }
> >
Добавить {tags.add ? 'Добавить' : 'Изменить'}
</button> </button>
</div> </div>
)} )}
@ -695,6 +758,7 @@ export const ProjectTracker = () => {
projectName={projectBoard.name} projectName={projectBoard.name}
projectUsers={projectBoard.projectUsers} projectUsers={projectBoard.projectUsers}
projectOwnerId={projectBoard.owner_id} projectOwnerId={projectBoard.owner_id}
projectMarks={projectBoard.mark}
/> />
)} )}
@ -827,6 +891,14 @@ export const ProjectTracker = () => {
/> />
)} )}
</div> </div>
{Boolean(task.mark.length) &&
<div className='tasks__board__item__tags'>
{task.mark.map((tag) => {
return <div className='tagItem' key={tag.id} style={{background: tag.color}}><p>{tag.slug}</p></div>
})
}
</div>
}
{task.dead_line && ( {task.dead_line && (
<div className="tasks__board__item__deadLine"> <div className="tasks__board__item__deadLine">
<p>Срок исполнения:</p> <p>Срок исполнения:</p>

View File

@ -386,7 +386,7 @@
.persons__list { .persons__list {
position: absolute; position: absolute;
z-index: 20; z-index: 8;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background: linear-gradient(180deg, #ffffff 0%, #ebebeb 100%); background: linear-gradient(180deg, #ffffff 0%, #ebebeb 100%);
@ -784,7 +784,7 @@
&__list { &__list {
position: absolute; position: absolute;
background: #f8f9fa; background: #f8f9fa;
z-index: 20; z-index: 8;
border-radius: 8px; border-radius: 8px;
padding: 20px 10px 10px; padding: 20px 10px 10px;
top: 30px; top: 30px;
@ -808,6 +808,7 @@
margin-top: 8px; margin-top: 8px;
.tagItem { .tagItem {
position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 5px; padding: 5px;
@ -835,6 +836,23 @@
border-radius: 50px; border-radius: 50px;
} }
} }
&__images {
position: absolute;
right: 5px;
top: 3px;
display: flex;
column-gap: 3px;
img {
cursor: pointer;
}
.delete {
width: 14px;
height: 14px;
}
}
} }
} }
@ -999,7 +1017,6 @@
&__item { &__item {
width: 328px; width: 328px;
max-height: 250px;
padding: 16px; padding: 16px;
position: relative; position: relative;
box-shadow: 0px 3px 2px -2px rgba(0, 0, 0, 0.06), box-shadow: 0px 3px 2px -2px rgba(0, 0, 0, 0.06),
@ -1147,6 +1164,21 @@
height: 25px; height: 25px;
} }
} }
&__tags {
display: flex;
flex-wrap: wrap;
column-gap: 6px;
row-gap: 3px;
margin: 5px 0 10px;
.tagItem {
padding: 3px 10px;
border-radius: 10px;
color: white;
font-size: 12px;
}
}
} }
.openItems { .openItems {

View File

@ -42,6 +42,9 @@ export const projectsTrackerSlice = createSlice({
(person) => person.user_id !== action.payload (person) => person.user_id !== action.payload
); );
}, },
deleteTagProject: (state, action) => {
state.projectBoard.mark = state.projectBoard.mark.filter((tag) => tag.id !== action.payload)
},
addPersonToProject: (state, action) => { addPersonToProject: (state, action) => {
state.projectBoard.projectUsers.push(action.payload); state.projectBoard.projectUsers.push(action.payload);
}, },
@ -181,6 +184,7 @@ export const {
setColumnId, setColumnId,
setColumnPriority, setColumnPriority,
deletePersonOnProject, deletePersonOnProject,
deleteTagProject,
addPersonToProject, addPersonToProject,
addNewTagToProject, addNewTagToProject,
filterCreatedByMe, filterCreatedByMe,