Merge pull request #118 from apuc/tracker-tasks

Tracker tasks
This commit is contained in:
NikoM1k 2023-07-14 03:04:19 +03:00 committed by GitHub
commit 94e3c43c4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 352 additions and 24 deletions

View File

@ -6,6 +6,8 @@ import {
Navigate, Navigate,
} from "react-router-dom"; } from "react-router-dom";
import { getNotification } from "@redux/outstaffingSlice";
import AuthForPartners from "./pages/AuthForPartners/AuthForPartners"; import AuthForPartners from "./pages/AuthForPartners/AuthForPartners";
import AuthForDevelopers from "./pages/AuthForDevelopers/AuthForDevelopers"; import AuthForDevelopers from "./pages/AuthForDevelopers/AuthForDevelopers";
import { TrackerIntro } from "./pages/TrackerIntro/TrackerIntro" import { TrackerIntro } from "./pages/TrackerIntro/TrackerIntro"
@ -42,12 +44,15 @@ import Blog from "./pages/Blog/Blog";
import { ProjectTracker } from "./pages/ProjectTracker/ProjectTracker"; import { ProjectTracker } from "./pages/ProjectTracker/ProjectTracker";
import { FrequentlyAskedQuestions } from "./pages/FrequentlyAskedQuestions/FrequentlyAskedQuestions"; import { FrequentlyAskedQuestions } from "./pages/FrequentlyAskedQuestions/FrequentlyAskedQuestions";
import { FrequentlyAskedQuestion } from "./pages/FrequentlyAskedQuestion/FrequentlyAskedQuestion"; import { FrequentlyAskedQuestion } from "./pages/FrequentlyAskedQuestion/FrequentlyAskedQuestion";
import Notification from "@components/Notification/Notification";
import { useSelector } from "react-redux";
import "./assets/global.scss"; import "./assets/global.scss";
import "./assets/fonts/stylesheet.css"; import "./assets/fonts/stylesheet.css";
import "bootstrap/dist/css/bootstrap.min.css"; import "bootstrap/dist/css/bootstrap.min.css";
const App = () => { const App = () => {
const notification = useSelector(getNotification)
return ( return (
<> <>
<Router> <Router>
@ -130,6 +135,9 @@ const App = () => {
<Route path="*" element={<Navigate to="/auth" replace />} /> <Route path="*" element={<Navigate to="/auth" replace />} />
</Routes> </Routes>
</Router> </Router>
{notification.show &&
<Notification />
}
</> </>
); );
}; };

View File

@ -0,0 +1,3 @@
<svg width="17" height="20" viewBox="0 0 17 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.6068 6.47127C14.8156 5.48474 14.0893 4.57969 13.3639 3.67463C12.6892 2.83354 12.0085 1.9977 11.3452 1.14785C11.1963 0.956849 11.0465 0.880625 10.802 0.881501C8.01765 0.890263 5.23328 0.885006 2.44891 0.888511C1.49041 0.889387 0.891129 1.47903 0.890253 2.43227C0.886748 7.48146 0.899014 12.5307 0.877987 17.5798C0.874482 18.5051 1.62971 19.121 2.39809 19.1157C6.45111 19.0868 10.5033 19.1035 14.5563 19.1008C15.5069 19.1008 16.0606 18.5375 16.0615 17.5746C16.0641 14.6851 16.0615 11.7956 16.0632 8.90606C16.0632 8.76063 16.0518 8.60818 16.0904 8.4715C16.1509 8.25422 16.3086 8.12542 16.5469 8.14908C16.7773 8.17186 16.9201 8.29715 16.9341 8.53984C16.9403 8.64147 16.9385 8.74398 16.9385 8.84649C16.9385 11.736 16.8991 14.6264 16.956 17.5141C16.9823 18.8695 15.8495 20.019 14.4608 19.9997C10.4673 19.9437 6.47126 19.9463 2.47782 19.9997C1.16887 20.019 -0.0121712 18.9387 9.47065e-05 17.5474C0.0447778 12.528 0.0421494 7.50775 0.00184699 2.48747C-0.00954283 1.05322 1.12068 -0.0121611 2.5006 0.00010484C5.35769 0.0246368 8.21478 0.0132469 11.0719 0.00098098C11.4136 -0.000771299 11.6396 0.113127 11.849 0.376845C13.4637 2.40511 15.089 4.42636 16.7151 6.44586C16.878 6.64825 17.0305 6.84889 16.8973 7.10998C16.7729 7.35442 16.5267 7.35179 16.2858 7.35092C14.9882 7.34654 13.6907 7.35267 12.3931 7.34741C11.4907 7.34391 10.8896 6.74112 10.8861 5.84396C10.8826 4.99761 10.8782 4.15126 10.8931 3.3049C10.8975 3.06046 10.8318 2.98248 10.5804 2.98599C9.60346 3.00001 8.62657 3.00614 7.65055 2.98511C7.22825 2.97635 7.36843 3.25934 7.36142 3.45735C7.35441 3.6501 7.25541 3.88666 7.64179 3.89192C8.66336 3.90681 8.66336 3.92609 8.66336 4.94767C8.66336 5.28323 8.66336 5.61879 8.66336 5.95435C8.66336 6.32934 8.49777 6.53085 8.10001 6.49142C8.02816 6.48441 7.95457 6.49055 7.88185 6.49055C7.39296 6.49055 7.19758 6.78143 7.35704 7.24841C7.39734 7.36669 7.47357 7.35617 7.55592 7.35705C7.73115 7.35968 7.9055 7.36055 8.08073 7.35705C8.48025 7.34916 8.66774 7.54542 8.66424 7.94231C8.65986 8.42419 8.67037 8.90519 8.66161 9.38707C8.65373 9.86369 8.53107 9.97934 8.06146 9.98635C7.94493 9.9881 7.82665 9.99949 7.71188 9.98372C7.43589 9.94604 7.30885 10.0354 7.35354 10.3333C7.40172 10.6496 7.27994 10.9203 7.12924 11.198C6.76039 11.8779 6.45724 12.5832 6.49317 13.3858C6.5212 14.0236 6.91634 14.4424 7.51825 14.4625C8.15257 14.4836 8.58889 14.0911 8.63533 13.441C8.68614 12.7155 8.39439 12.0794 8.08774 11.4504C7.99837 11.2664 7.97559 11.0903 8.09913 10.9142C8.20777 10.76 8.36723 10.7644 8.53107 10.7679C8.68527 10.7714 8.77726 10.8677 8.83509 10.9843C9.24424 11.8087 9.60171 12.6437 9.50358 13.6039C9.4002 14.6089 8.5959 15.3431 7.55943 15.3413C6.53434 15.3396 5.70464 14.5852 5.62316 13.5855C5.55394 12.7348 5.7318 11.9314 6.13921 11.1919C6.39329 10.7319 6.52295 10.2728 6.48966 9.74365C6.45724 9.23111 6.60706 9.11283 7.13012 9.10845C7.18882 9.10845 7.24752 9.11459 7.30447 9.1067C7.45692 9.0848 7.70136 9.19081 7.74605 9.04362C7.81526 8.8167 7.79336 8.54948 7.75568 8.30766C7.73466 8.17274 7.55505 8.23231 7.45079 8.23757C6.3267 8.29189 6.48616 8.05884 6.48178 7.23527C6.48002 6.89971 6.47827 6.56414 6.48178 6.22858C6.48791 5.77211 6.62897 5.6293 7.0758 5.61528C7.14852 5.61353 7.22124 5.61528 7.29483 5.61528C7.73203 5.61528 7.94405 5.27359 7.76707 4.86618C7.69173 4.69359 7.54541 4.75141 7.42801 4.75667C6.34773 4.80573 6.4809 4.58144 6.4809 3.77451C6.4809 3.39515 6.48441 3.01578 6.4809 2.63641C6.47652 2.29559 6.63861 2.11248 6.97855 2.1116C8.40753 2.1081 9.83564 2.10547 11.2646 2.11335C11.6212 2.11511 11.7737 2.32188 11.771 2.67934C11.7614 3.71581 11.7623 4.75141 11.764 5.78788C11.7649 6.30743 11.9244 6.46864 12.4378 6.4704C13.4558 6.4739 14.473 6.47127 15.6076 6.47127H15.6068Z" fill="#121313"/>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.2828 0.743646C10.1778 0.743646 9.25517 1.11731 8.49082 1.86683C7.82592 2.51909 7.17146 3.18288 6.51096 3.84009C6.37303 3.97746 6.22137 4.05879 6.02355 3.94669C5.8631 3.85548 5.83123 3.7159 5.85266 3.5494C5.86145 3.48236 5.89222 3.42796 5.93838 3.3829C6.75494 2.58558 7.49567 1.70472 8.38916 0.99257C11.0977 -1.16752 15.2524 0.392514 15.8921 3.79558C16.202 5.44573 15.7657 6.87828 14.5925 8.0795C13.9606 8.72681 13.316 9.36203 12.6736 9.99946C12.4736 10.1978 12.2159 10.2055 12.0785 10.0203C11.9203 9.80713 12.0071 9.63239 12.1736 9.46699C12.7835 8.86199 13.383 8.24709 13.9996 7.64924C14.6557 7.01181 15.092 6.2513 15.2162 5.35012C15.5003 3.28949 14.3298 1.58219 12.589 0.966194C12.35 0.881571 12.101 0.818378 11.851 0.774967C11.6548 0.740898 11.4504 0.751888 11.2823 0.744745L11.2828 0.743646Z" fill="#0D0E0E"/>
<path d="M4.60639 15.2565C5.81474 15.256 6.73735 14.8823 7.50116 14.1334C8.16661 13.4811 8.82107 12.8173 9.48047 12.159C9.62004 12.02 9.77116 11.9408 9.96788 12.0529C10.1283 12.1442 10.1569 12.2854 10.1366 12.4513C10.1283 12.5189 10.0965 12.5728 10.0503 12.6178C9.23484 13.4168 8.49466 14.2993 7.59952 15.0087C4.87949 17.1639 0.75163 15.6137 0.0988219 12.2024C-0.212196 10.577 0.21147 9.15212 1.36213 7.95916C2.00285 7.29481 2.66335 6.65024 3.31835 6.00018C3.53925 5.78093 3.80631 5.79741 3.94094 6.02711C4.06348 6.23592 3.96182 6.38868 3.8173 6.5332C3.19636 7.15249 2.57378 7.77013 1.95614 8.39271C0.212569 10.1511 0.335108 12.8437 2.24078 14.3916C2.96393 14.979 3.80301 15.2736 4.60639 15.2565Z" fill="#0D0E0E"/>
<path d="M11.2779 5.06932C11.2658 5.2622 11.1416 5.3732 11.0251 5.48969C9.57169 6.94258 8.11825 8.39601 6.66482 9.84944C6.23841 10.2759 5.8065 10.6962 5.38558 11.1276C5.09544 11.4254 4.78442 11.2166 4.72783 10.9737C4.68716 10.7973 4.79706 10.6808 4.90806 10.5698C5.60538 9.87197 6.3038 9.17465 7.00167 8.47679C8.19024 7.28821 9.37716 6.09744 10.5679 4.91107C10.8432 4.63687 11.1235 4.66929 11.2449 4.97701C11.2581 5.01053 11.2696 5.0446 11.2784 5.06878L11.2779 5.06932Z" fill="#0D0E0E"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24 12.0313C23.9917 18.6789 18.6282 24.0158 11.9708 24C5.32262 23.9841 -0.0166358 18.6197 3.89507e-05 11.9721C0.0167137 5.32946 5.38848 -0.0158064 12.0317 3.51207e-05C18.6782 0.0158767 24.0083 5.37365 24 12.0313ZM22.7494 12.0121C22.7536 6.04984 18.0029 1.26652 12.0609 1.25068C6.02212 1.23484 1.25398 5.98313 1.25231 12.0138C1.25064 17.976 6.00795 22.7026 11.9617 22.761C17.6669 22.8169 22.8219 18.1244 22.7502 12.0121H22.7494Z" fill="#8DC63F"/>
<path d="M11.9875 20.2673C7.49701 20.3382 3.69184 16.5228 3.72018 11.9446C3.74853 7.46732 7.49368 3.69453 12.0601 3.72038C16.5497 3.74622 20.3157 7.51484 20.2832 12.0597C20.2515 16.537 16.5172 20.3473 11.9867 20.2673H11.9875ZM15.3933 9.20071C15.3592 8.94808 15.2349 8.75965 14.9931 8.66294C14.7305 8.55788 14.5071 8.62958 14.3128 8.82385C13.6358 9.50004 12.9472 10.1645 12.2877 10.8582C12.0534 11.105 11.9208 11.07 11.7074 10.8466C11.0746 10.1854 10.4218 9.54339 9.77395 8.89556C9.4046 8.5262 9.08445 8.48784 8.80098 8.77049C8.51834 9.0523 8.55002 9.39748 8.90019 9.751C9.56301 10.4213 10.2225 11.0959 10.8978 11.7545C11.0921 11.9438 11.0921 12.058 10.8962 12.2465C10.2067 12.9101 9.53467 13.5922 8.85517 14.2658C8.64507 14.4743 8.5075 14.7002 8.6409 15.0062C8.83016 15.4406 9.32206 15.5157 9.69975 15.1488C10.3859 14.4818 11.0679 13.8106 11.7357 13.1252C11.9317 12.9243 12.045 12.8935 12.2568 13.1152C12.898 13.7873 13.5633 14.4368 14.2228 15.0904C14.5863 15.4506 14.9173 15.4848 15.1999 15.2047C15.485 14.922 15.4525 14.5877 15.0949 14.2275C14.4404 13.568 13.7909 12.9026 13.1206 12.2606C12.9071 12.0564 12.9096 11.9421 13.1206 11.7404C13.8001 11.0867 14.4562 10.4088 15.1265 9.746C15.2824 9.59175 15.38 9.41999 15.3917 9.20071H15.3933Z" fill="#5B6871"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
<defs>
</defs>
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: none; fill-rule: nonzero; opacity: 1;" transform="translate(1.4065934065934016 1.4065934065934016) scale(2.81 2.81)" >
<path d="M 89.328 2.625 L 89.328 2.625 c -1.701 -2.859 -5.728 -3.151 -7.824 -0.568 L 46.532 45.173 c -0.856 1.055 -2.483 0.997 -3.262 -0.115 l -8.382 -11.97 c -2.852 -4.073 -8.789 -4.335 -11.989 -0.531 l 0 0 c -2.207 2.624 -2.374 6.403 -0.408 9.211 l 17.157 24.502 c 2.088 2.982 6.507 2.977 8.588 -0.011 l 4.925 -7.07 L 89.135 7.813 C 90.214 6.272 90.289 4.242 89.328 2.625 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(111,178,6); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
<path d="M 45 90 C 20.187 90 0 69.813 0 45 C 0 20.187 20.187 0 45 0 c 6.072 0 11.967 1.19 17.518 3.538 c 2.034 0.861 2.986 3.208 2.125 5.242 c -0.859 2.035 -3.207 2.987 -5.242 2.126 C 54.842 8.978 49.996 8 45 8 C 24.598 8 8 24.598 8 45 c 0 20.402 16.598 37 37 37 c 20.402 0 37 -16.598 37 -37 c 0 -3.248 -0.42 -6.469 -1.249 -9.573 c -0.57 -2.134 0.698 -4.327 2.832 -4.897 c 2.133 -0.571 4.326 0.698 4.896 2.833 C 89.488 37.14 90 41.055 90 45 C 90 69.813 69.813 90 45 90 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(111,178,6); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,33 @@
import React from "react";
import close from "assets/icons/closeProjectPersons.svg";
import "./acceptModal.scss";
export const AcceptModal = ({ closeModal, agreeHandler }) => {
return (
<div className="backDrop">
<div className="acceptModal">
<h3 className="acceptModal__title">
Вы точно хотите переместить задачу в архив?
</h3>
<div className="acceptModal__buttons">
<button className="agree" onClick={agreeHandler}>
Да
</button>
<button className="cancel" onClick={closeModal}>
Нет
</button>
</div>
<img
className="acceptModal__close"
src={close}
alt="close"
onClick={closeModal}
/>
</div>
</div>
);
};
export default AcceptModal;

View File

@ -0,0 +1,61 @@
.backDrop {
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, 0.11);
position: fixed;
top: 0;
left: 0;
display: flex;
z-index: 11;
align-items: center;
justify-content: center;
.acceptModal {
border-radius: 20px;
background: linear-gradient(180deg, #FFF 0%, #EBEBEB 100%);
padding: 50px 34px 25px;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
row-gap: 25px;
&__title {
max-width: 260px;
font-size: 18px;
font-weight: 500;
text-align: center;
margin-bottom: 0;
}
&__buttons {
display: flex;
column-gap: 20px;
button {
min-width: 90px;
height: 37px;
border-radius: 44px;
border: none;
font-size: 14px;
font-weight: 500;
color: white;
}
.agree {
background: #52B709;
}
.cancel {
background: #B0BABF;
}
}
&__close {
position: absolute;
top: 15px;
right: 22px;
cursor: pointer;
}
}
}

View File

@ -20,6 +20,9 @@ import {
import { apiRequest } from "@api/request"; import { apiRequest } from "@api/request";
import { useNotification } from "@hooks/useNotification";
import AcceptModal from "@components/Modal/AcceptModal/AcceptModal";
import TrackerModal from "@components/Modal/Tracker/TrackerModal/TrackerModal"; import TrackerModal from "@components/Modal/Tracker/TrackerModal/TrackerModal";
import TrackerTaskComment from "@components/TrackerTaskComment/TrackerTaskComment"; import TrackerTaskComment from "@components/TrackerTaskComment/TrackerTaskComment";
@ -82,6 +85,8 @@ export const ModalTiсket = ({
const [correctProjectUsers, setCorrectProjectUsers] = useState(projectUsers); const [correctProjectUsers, setCorrectProjectUsers] = useState(projectUsers);
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 { showNotification } = useNotification();
function deleteTask() { function deleteTask() {
apiRequest("/task/update-task", { apiRequest("/task/update-task", {
@ -93,7 +98,16 @@ export const ModalTiсket = ({
}).then(() => { }).then(() => {
setActive(false); setActive(false);
dispatch(setProjectBoardFetch(projectId)); dispatch(setProjectBoardFetch(projectId));
showNotification({
show: true,
text: "Задача успешно была перемещена в архив",
type: "archive",
}); });
});
}
function archiveTask() {
setAcceptModalOpen(true);
} }
function editTask() { function editTask() {
@ -416,6 +430,11 @@ export const ModalTiсket = ({
navigator.clipboard.writeText( navigator.clipboard.writeText(
`https://itguild.info/tracker/task/${task.id}` `https://itguild.info/tracker/task/${task.id}`
); );
showNotification({
show: true,
text: "Ссылка скопирована в буфер обмена",
type: "copy",
});
} }
function selectDeadLine(date) { function selectDeadLine(date) {
@ -430,6 +449,10 @@ export const ModalTiсket = ({
}); });
} }
function closeAcceptModal() {
setAcceptModalOpen(false);
}
return ( return (
<div <div
className={active ? "modal-tiket active" : "modal-tiket"} className={active ? "modal-tiket active" : "modal-tiket"}
@ -809,7 +832,7 @@ export const ModalTiсket = ({
<img src={link}></img> <img src={link}></img>
<p onClick={copyTicketLink}>ссылка на задачу</p> <p onClick={copyTicketLink}>ссылка на задачу</p>
</div> </div>
<div onClick={deleteTask}> <div onClick={archiveTask}>
<img src={archive}></img> <img src={archive}></img>
<p>в архив</p> <p>в архив</p>
</div> </div>
@ -819,8 +842,13 @@ export const ModalTiсket = ({
</div> </div>
</div> </div>
</div> </div>
{acceptModalOpen && (
<AcceptModal
closeModal={closeAcceptModal}
agreeHandler={deleteTask}
/>
)}
</div> </div>
<TrackerModal <TrackerModal
active={addSubtask} active={addSubtask}
setActive={setAddSubtask} setActive={setAddSubtask}

View File

@ -689,6 +689,7 @@
.react-datepicker-popper { .react-datepicker-popper {
top: 10px !important; top: 10px !important;
left: -110px !important; left: -110px !important;
z-index: 10;
} }
.react-datepicker__current-month { .react-datepicker__current-month {

View File

@ -26,6 +26,7 @@ import { apiRequest } from "@api/request";
import { getCorrectDate } from "@components/Calendar/calendarHelper"; import { getCorrectDate } from "@components/Calendar/calendarHelper";
import { Footer } from "@components/Common/Footer/Footer"; import { Footer } from "@components/Common/Footer/Footer";
import { Loader } from "@components/Common/Loader/Loader"; import { Loader } from "@components/Common/Loader/Loader";
import AcceptModal from "@components/Modal/AcceptModal/AcceptModal";
import TrackerModal from "@components/Modal/Tracker/TrackerModal/TrackerModal"; import TrackerModal from "@components/Modal/Tracker/TrackerModal/TrackerModal";
import { Navigation } from "@components/Navigation/Navigation"; import { Navigation } from "@components/Navigation/Navigation";
import { ProfileBreadcrumbs } from "@components/ProfileBreadcrumbs/ProfileBreadcrumbs"; import { ProfileBreadcrumbs } from "@components/ProfileBreadcrumbs/ProfileBreadcrumbs";
@ -82,6 +83,7 @@ export const TicketFullScreen = () => {
const [startDate, setStartDate] = useState(null); const [startDate, setStartDate] = useState(null);
const [uploadedFile, setUploadedFile] = useState(null); const [uploadedFile, setUploadedFile] = useState(null);
const [taskFiles, setTaskFiles] = useState([]); const [taskFiles, setTaskFiles] = useState([]);
const [acceptModalOpen, setAcceptModalOpen] = useState(false);
useEffect(() => { useEffect(() => {
apiRequest(`/task/get-task?task_id=${ticketId.id}`).then((taskInfo) => { apiRequest(`/task/get-task?task_id=${ticketId.id}`).then((taskInfo) => {
@ -161,6 +163,10 @@ export const TicketFullScreen = () => {
}); });
} }
function archiveTask() {
setAcceptModalOpen(true);
}
function editTask() { function editTask() {
apiRequest("/task/update-task", { apiRequest("/task/update-task", {
method: "PUT", method: "PUT",
@ -450,6 +456,10 @@ export const TicketFullScreen = () => {
}); });
} }
function closeAcceptModal() {
setAcceptModalOpen(false);
}
return ( return (
<section className="ticket-full-screen"> <section className="ticket-full-screen">
<ProfileHeader /> <ProfileHeader />
@ -976,7 +986,7 @@ export const TicketFullScreen = () => {
<img src={link} alt="link"></img> <img src={link} alt="link"></img>
<p onClick={copyTicketLink}>ссылка на задачу</p> <p onClick={copyTicketLink}>ссылка на задачу</p>
</div> </div>
<div> <div onClick={archiveTask}>
<img src={archive} alt="arch"></img> <img src={archive} alt="arch"></img>
<p>в архив</p> <p>в архив</p>
</div> </div>
@ -990,6 +1000,9 @@ export const TicketFullScreen = () => {
</> </>
)} )}
</div> </div>
{acceptModalOpen && (
<AcceptModal closeModal={closeAcceptModal} agreeHandler={deleteTask} />
)}
<Footer /> <Footer />
</section> </section>
); );

View File

@ -23,6 +23,8 @@ import { urlForLocal } from "@utils/helper";
import { apiRequest } from "@api/request"; import { apiRequest } from "@api/request";
import { useNotification } from "@hooks/useNotification";
import BaseButton from "@components/Common/BaseButton/BaseButton"; import BaseButton from "@components/Common/BaseButton/BaseButton";
import ModalLayout from "@components/Common/ModalLayout/ModalLayout"; import ModalLayout from "@components/Common/ModalLayout/ModalLayout";
@ -67,9 +69,11 @@ export const TrackerModal = ({
const [correctProjectUsers, setCorrectProjectUsers] = useState([]); const [correctProjectUsers, setCorrectProjectUsers] = useState([]);
const [selectColumnPriorityOpen, setSelectColumnPriorityOpen] = const [selectColumnPriorityOpen, setSelectColumnPriorityOpen] =
useState(false); useState(false);
const { showNotification } = useNotification();
function createTab() { function createTab() {
if (!valueColumn) { if (!valueColumn) {
showNotification({ show: true, text: "Введите название", type: "error" });
return; return;
} }
@ -91,6 +95,11 @@ export const TrackerModal = ({
function createTiket() { function createTiket() {
if (!valueTiket || !descriptionTicket) { if (!valueTiket || !descriptionTicket) {
showNotification({
show: true,
text: "Введите название и описание",
type: "error",
});
return; return;
} }
@ -106,6 +115,13 @@ export const TrackerModal = ({
priority: priorityTask, priority: priorityTask,
}, },
}).then((res) => { }).then((res) => {
if (res.status === 500) {
showNotification({
show: true,
text: "Задача с таким именем уже существует",
type: "error",
});
} else {
if (selectedExecutorTask.user_id) { if (selectedExecutorTask.user_id) {
apiRequest("/task/update-task", { apiRequest("/task/update-task", {
method: "PUT", method: "PUT",
@ -126,6 +142,12 @@ export const TrackerModal = ({
setDescriptionTicket("Описание задачи"); setDescriptionTicket("Описание задачи");
dispatch(setProjectBoardFetch(projectBoard.id)); dispatch(setProjectBoardFetch(projectBoard.id));
} }
showNotification({
show: true,
text: "Задача создана",
type: "success",
});
}
}); });
} }
@ -204,10 +226,18 @@ export const TrackerModal = ({
status: 19, status: 19,
}, },
}).then((res) => { }).then((res) => {
if (!Array.isArray(res.name)) {
const result = { ...res, columns: [] }; const result = { ...res, columns: [] };
dispatch(setProject(result)); dispatch(setProject(result));
setActive(false); setActive(false);
setNameProject(""); setNameProject("");
} else {
showNotification({
show: true,
text: "Проект с таким именем уже существует",
type: "error",
});
}
}); });
} }
} }

View File

@ -0,0 +1,40 @@
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { closeNotification, getNotification } from "@redux/outstaffingSlice";
import archive from "assets/icons/archiveNotification.svg";
import close from "assets/icons/closeProjectPersons.svg";
import copy from "assets/icons/copyNotification.svg";
import error from "assets/icons/errorNotification.svg";
import success from "assets/icons/successNotification.svg";
import "./notification.scss";
const images = {
archive: archive,
error: error,
copy: copy,
success: success,
};
export const Notification = () => {
const dispatch = useDispatch();
const notificationInfo = useSelector(getNotification);
return (
<div className="notification">
<div className="notification__info">
<img src={images[notificationInfo.type]} alt="img" />
<h2>{notificationInfo.text}</h2>
</div>
<img
onClick={() => dispatch(closeNotification())}
className="notification__close"
src={close}
alt="close"
/>
</div>
);
};
export default Notification;

View File

@ -0,0 +1,35 @@
.notification {
border-radius: 40px;
background: linear-gradient(180deg, #FFF 0%, #EBEBEB 100%);
padding: 20px 82px 17px 27px;
position: fixed;
bottom: 25px;
right: 25px;
z-index: 20;
&__info {
display: flex;
column-gap: 10px;
align-items: center;
h2 {
max-width: 194px;
font-weight: 500;
font-size: 16px;
margin-bottom: 0;
}
img {
max-width: 24px;
}
}
&__close {
cursor: pointer;
position: absolute;
top: 15px;
right: 25px;
width: 15px;
height: 15px;
}
}

View File

@ -6,6 +6,9 @@ import { deleteProject, modalToggle } from "@redux/projectsTrackerSlice";
import { apiRequest } from "@api/request"; import { apiRequest } from "@api/request";
import { useNotification } from "@hooks/useNotification";
import AcceptModal from "@components/Modal/AcceptModal/AcceptModal";
import { ModalSelect } from "@components/Modal/ModalSelect/ModalSelect"; import { ModalSelect } from "@components/Modal/ModalSelect/ModalSelect";
import TrackerModal from "@components/Modal/Tracker/TrackerModal/TrackerModal"; import TrackerModal from "@components/Modal/Tracker/TrackerModal/TrackerModal";
@ -19,7 +22,9 @@ import "./projectTiket.scss";
export const ProjectTiket = ({ project, index }) => { export const ProjectTiket = ({ project, index }) => {
const [modalSelect, setModalSelect] = useState(false); const [modalSelect, setModalSelect] = useState(false);
const [modalAdd, setModalAdd] = useState(false); const [modalAdd, setModalAdd] = useState(false);
const [acceptModalOpen, setAcceptModalOpen] = useState(false);
const dispatch = useDispatch(); const dispatch = useDispatch();
const { showNotification } = useNotification();
useEffect(() => { useEffect(() => {
initListeners(); initListeners();
@ -49,6 +54,11 @@ export const ProjectTiket = ({ project, index }) => {
}, },
}).then(() => { }).then(() => {
dispatch(deleteProject(project)); dispatch(deleteProject(project));
showNotification({
show: true,
text: "Проект успешно была перемещена в архив",
type: "archive",
});
}); });
} }
@ -58,6 +68,10 @@ export const ProjectTiket = ({ project, index }) => {
); );
} }
function closeAcceptModal() {
setAcceptModalOpen(false);
}
return ( return (
<div className="project" key={index}> <div className="project" key={index}>
<Link to={`/tracker/project/${project.id}`}>{project.name}</Link> <Link to={`/tracker/project/${project.id}`}>{project.name}</Link>
@ -98,7 +112,12 @@ export const ProjectTiket = ({ project, index }) => {
<img src={link}></img> <img src={link}></img>
<p onClick={copyProjectLink}>ссылка на проект</p> <p onClick={copyProjectLink}>ссылка на проект</p>
</div> </div>
<div> <div
onClick={() => {
setModalSelect(false);
setAcceptModalOpen(true);
}}
>
<img src={archiveSet}></img> <img src={archiveSet}></img>
<p>в архив</p> <p>в архив</p>
</div> </div>
@ -108,6 +127,12 @@ export const ProjectTiket = ({ project, index }) => {
</div> </div>
</div> </div>
</ModalSelect> </ModalSelect>
{acceptModalOpen && (
<AcceptModal
closeModal={closeAcceptModal}
agreeHandler={removeProject}
/>
)}
</div> </div>
); );
}; };

View File

@ -0,0 +1,15 @@
import { useDispatch } from "react-redux";
import { closeNotification, setNotification } from "../redux/outstaffingSlice";
export const useNotification = () => {
const dispatch = useDispatch();
const showNotification = (notification) => {
dispatch(setNotification(notification));
setTimeout(() => {
dispatch(closeNotification());
}, 2500);
};
return { showNotification };
};

View File

@ -25,6 +25,8 @@ import { caseOfNum } from "@utils/helper";
import { apiRequest } from "@api/request"; import { apiRequest } from "@api/request";
import { useNotification } from "@hooks/useNotification";
import BaseButton from "@components/Common/BaseButton/BaseButton"; import BaseButton from "@components/Common/BaseButton/BaseButton";
import { Footer } from "@components/Common/Footer/Footer"; import { Footer } from "@components/Common/Footer/Footer";
import { Loader } from "@components/Common/Loader/Loader"; import { Loader } from "@components/Common/Loader/Loader";
@ -72,6 +74,7 @@ export const ProjectTracker = () => {
const startWrapperIndexTest = useRef({}); const startWrapperIndexTest = useRef({});
const projectBoard = useSelector(getProjectBoard); const projectBoard = useSelector(getProjectBoard);
const loader = useSelector(getBoarderLoader); const loader = useSelector(getBoarderLoader);
const { showNotification } = useNotification();
useEffect(() => { useEffect(() => {
dispatch(activeLoader()); dispatch(activeLoader());
@ -224,6 +227,7 @@ export const ProjectTracker = () => {
} else { } else {
dispatch(setProjectBoardFetch(projectBoard.id)); dispatch(setProjectBoardFetch(projectBoard.id));
} }
showNotification({ show: true, text: "Колонка удалена", type: "error" });
}); });
} }

View File

@ -1,4 +1,4 @@
import { createSlice } from "@reduxjs/toolkit"; import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
const initialState = { const initialState = {
tags: [], tags: [],
@ -14,6 +14,11 @@ const initialState = {
partnerRequestId: null, partnerRequestId: null,
partnerRequests: [], partnerRequests: [],
partnerRequestInfo: {}, partnerRequestInfo: {},
notification: {
show: false,
text: "",
type: "",
},
}; };
export const outstaffingSlice = createSlice({ export const outstaffingSlice = createSlice({
@ -62,6 +67,12 @@ export const outstaffingSlice = createSlice({
setPartnerRequestInfo: (state, action) => { setPartnerRequestInfo: (state, action) => {
state.partnerRequestInfo = action.payload; state.partnerRequestInfo = action.payload;
}, },
setNotification: (state, action) => {
state.notification = action.payload;
},
closeNotification: (state) => {
state.notification.show = false;
},
}, },
}); });
@ -80,12 +91,15 @@ export const {
setPartnerRequestId, setPartnerRequestId,
setPartnerRequests, setPartnerRequests,
setPartnerRequestInfo, setPartnerRequestInfo,
setNotification,
closeNotification,
} = outstaffingSlice.actions; } = outstaffingSlice.actions;
export const selectProfiles = (state) => state.outstaffing.profiles; export const selectProfiles = (state) => state.outstaffing.profiles;
export const selectTags = (state) => state.outstaffing.tags; export const selectTags = (state) => state.outstaffing.tags;
export const selectFilteredCandidates = (state) => export const selectFilteredCandidates = (state) =>
state.outstaffing.filteredCandidates; state.outstaffing.filteredCandidates;
export const getNotification = (state) => state.outstaffing.notification;
export const selectItems = (state) => state.outstaffing.selectedItems; export const selectItems = (state) => state.outstaffing.selectedItems;
export const selectCurrentCandidate = (state) => export const selectCurrentCandidate = (state) =>
state.outstaffing.currentCandidate; state.outstaffing.currentCandidate;