diff --git a/src/App.js b/src/App.js
index 67b53b2e..8c987891 100644
--- a/src/App.js
+++ b/src/App.js
@@ -6,6 +6,8 @@ import {
Navigate,
} from "react-router-dom";
+import { getNotification } from "@redux/outstaffingSlice";
+
import AuthForPartners from "./pages/AuthForPartners/AuthForPartners";
import AuthForDevelopers from "./pages/AuthForDevelopers/AuthForDevelopers";
import { TrackerIntro } from "./pages/TrackerIntro/TrackerIntro"
@@ -42,12 +44,15 @@ import Blog from "./pages/Blog/Blog";
import { ProjectTracker } from "./pages/ProjectTracker/ProjectTracker";
import { FrequentlyAskedQuestions } from "./pages/FrequentlyAskedQuestions/FrequentlyAskedQuestions";
import { FrequentlyAskedQuestion } from "./pages/FrequentlyAskedQuestion/FrequentlyAskedQuestion";
+import Notification from "@components/Notification/Notification";
+import { useSelector } from "react-redux";
import "./assets/global.scss";
import "./assets/fonts/stylesheet.css";
import "bootstrap/dist/css/bootstrap.min.css";
const App = () => {
+ const notification = useSelector(getNotification)
return (
<>
@@ -130,6 +135,9 @@ const App = () => {
} />
+ {notification.show &&
+
+ }
>
);
};
diff --git a/src/assets/icons/archiveNotification.svg b/src/assets/icons/archiveNotification.svg
new file mode 100644
index 00000000..c52c04a9
--- /dev/null
+++ b/src/assets/icons/archiveNotification.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/icons/copyNotification.svg b/src/assets/icons/copyNotification.svg
new file mode 100644
index 00000000..4d94ea9b
--- /dev/null
+++ b/src/assets/icons/copyNotification.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/assets/icons/errorNotification.svg b/src/assets/icons/errorNotification.svg
new file mode 100644
index 00000000..af935231
--- /dev/null
+++ b/src/assets/icons/errorNotification.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/assets/icons/successNotification.svg b/src/assets/icons/successNotification.svg
new file mode 100644
index 00000000..1e3c3af9
--- /dev/null
+++ b/src/assets/icons/successNotification.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/src/components/Modal/AcceptModal/AcceptModal.jsx b/src/components/Modal/AcceptModal/AcceptModal.jsx
new file mode 100644
index 00000000..f791222a
--- /dev/null
+++ b/src/components/Modal/AcceptModal/AcceptModal.jsx
@@ -0,0 +1,24 @@
+import React from "react";
+
+import close from "assets/icons/closeProjectPersons.svg";
+
+import './acceptModal.scss'
+
+export const AcceptModal = ({closeModal, agreeHandler}) => {
+ return (
+
+
+
+ Вы точно хотите переместить задачу в архив?
+
+
+
+
+
+
+
+
+ )
+}
+
+export default AcceptModal
diff --git a/src/components/Modal/AcceptModal/acceptModal.scss b/src/components/Modal/AcceptModal/acceptModal.scss
new file mode 100644
index 00000000..5f1abce2
--- /dev/null
+++ b/src/components/Modal/AcceptModal/acceptModal.scss
@@ -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;
+ }
+ }
+}
diff --git a/src/components/Modal/Tracker/ModalTicket/ModalTicket.jsx b/src/components/Modal/Tracker/ModalTicket/ModalTicket.jsx
index 210445e3..b1624278 100644
--- a/src/components/Modal/Tracker/ModalTicket/ModalTicket.jsx
+++ b/src/components/Modal/Tracker/ModalTicket/ModalTicket.jsx
@@ -9,6 +9,7 @@ import { Link } from "react-router-dom";
import { getProfileInfo } from "@redux/outstaffingSlice";
import { setProjectBoardFetch } from "@redux/projectsTrackerSlice";
+import { useNotification } from "@hooks/useNotification";
import {
backendImg,
@@ -22,6 +23,7 @@ import { apiRequest } from "@api/request";
import TrackerModal from "@components/Modal/Tracker/TrackerModal/TrackerModal";
import TrackerTaskComment from "@components/TrackerTaskComment/TrackerTaskComment";
+import AcceptModal from "@components/Modal/AcceptModal/AcceptModal";
import archive from "assets/icons/archive.svg";
import arrow from "assets/icons/arrows/arrowStart.png";
@@ -82,6 +84,8 @@ export const ModalTiсket = ({
const [correctProjectUsers, setCorrectProjectUsers] = useState(projectUsers);
const [executorId, setExecutorId] = useState(task.executor_id);
const profileInfo = useSelector(getProfileInfo);
+ const [acceptModalOpen, setAcceptModalOpen] = useState(false)
+ const { showNotification } = useNotification()
function deleteTask() {
apiRequest("/task/update-task", {
@@ -93,9 +97,14 @@ export const ModalTiсket = ({
}).then(() => {
setActive(false);
dispatch(setProjectBoardFetch(projectId));
+ showNotification({show: true, text: 'Задача успешно была перемещена в архив', type: 'archive'})
});
}
+ function archiveTask () {
+ setAcceptModalOpen(true)
+ }
+
function editTask() {
apiRequest("/task/update-task", {
method: "PUT",
@@ -416,6 +425,7 @@ export const ModalTiсket = ({
navigator.clipboard.writeText(
`https://itguild.info/tracker/task/${task.id}`
);
+ showNotification({show: true, text: 'Ссылка скопирована в буфер обмена', type: 'copy'})
}
function selectDeadLine(date) {
@@ -430,6 +440,10 @@ export const ModalTiсket = ({
});
}
+ function closeAcceptModal () {
+ setAcceptModalOpen(false)
+ }
+
return (
-
+
в архив
@@ -819,8 +833,13 @@ export const ModalTiсket = ({
+ {acceptModalOpen &&
+
+ }
-
{
const [startDate, setStartDate] = useState(null);
const [uploadedFile, setUploadedFile] = useState(null);
const [taskFiles, setTaskFiles] = useState([]);
+ const [acceptModalOpen, setAcceptModalOpen] = useState(false)
useEffect(() => {
apiRequest(`/task/get-task?task_id=${ticketId.id}`).then((taskInfo) => {
@@ -161,6 +163,10 @@ export const TicketFullScreen = () => {
});
}
+ function archiveTask () {
+ setAcceptModalOpen(true)
+ }
+
function editTask() {
apiRequest("/task/update-task", {
method: "PUT",
@@ -450,6 +456,10 @@ export const TicketFullScreen = () => {
});
}
+ function closeAcceptModal () {
+ setAcceptModalOpen(false)
+ }
+
return (
@@ -976,7 +986,7 @@ export const TicketFullScreen = () => {
ссылка на задачу
-
+
в архив
@@ -990,6 +1000,12 @@ export const TicketFullScreen = () => {
>
)}
+ {acceptModalOpen &&
+
+ }
);
diff --git a/src/components/Modal/Tracker/TrackerModal/TrackerModal.jsx b/src/components/Modal/Tracker/TrackerModal/TrackerModal.jsx
index a640dc53..191abc4b 100644
--- a/src/components/Modal/Tracker/TrackerModal/TrackerModal.jsx
+++ b/src/components/Modal/Tracker/TrackerModal/TrackerModal.jsx
@@ -30,6 +30,7 @@ import arrowDown from "assets/icons/arrows/selectArrow.png";
import avatarMok from "assets/images/avatarMok.png";
import "./trackerModal.scss";
+import {useNotification} from "@hooks/useNotification";
export const TrackerModal = ({
active,
@@ -67,9 +68,11 @@ export const TrackerModal = ({
const [correctProjectUsers, setCorrectProjectUsers] = useState([]);
const [selectColumnPriorityOpen, setSelectColumnPriorityOpen] =
useState(false);
+ const { showNotification } = useNotification()
function createTab() {
if (!valueColumn) {
+ showNotification({show: true, text: 'Введите название', type: 'error'})
return;
}
@@ -91,6 +94,7 @@ export const TrackerModal = ({
function createTiket() {
if (!valueTiket || !descriptionTicket) {
+ showNotification({show: true, text: 'Введите название и описание', type: 'error'})
return;
}
@@ -106,25 +110,30 @@ export const TrackerModal = ({
priority: priorityTask,
},
}).then((res) => {
- if (selectedExecutorTask.user_id) {
- apiRequest("/task/update-task", {
- method: "PUT",
- data: {
- task_id: res.id,
- executor_id: selectedExecutorTask.user_id,
- },
- }).then(() => {
- dispatch(setProjectBoardFetch(projectBoard.id));
+ if (res.status === 500) {
+ showNotification({show: true, text: 'Задача с таким именем уже существует', type: 'error'})
+ } else {
+ if (selectedExecutorTask.user_id) {
+ apiRequest("/task/update-task", {
+ method: "PUT",
+ data: {
+ task_id: res.id,
+ executor_id: selectedExecutorTask.user_id,
+ },
+ }).then(() => {
+ dispatch(setProjectBoardFetch(projectBoard.id));
+ setActive(false);
+ setValueTiket("");
+ setDescriptionTicket("Описание задачи");
+ setSelectedExecutorTask("Выберите исполнителя задачи");
+ });
+ } else {
setActive(false);
setValueTiket("");
setDescriptionTicket("Описание задачи");
- setSelectedExecutorTask("Выберите исполнителя задачи");
- });
- } else {
- setActive(false);
- setValueTiket("");
- setDescriptionTicket("Описание задачи");
- dispatch(setProjectBoardFetch(projectBoard.id));
+ dispatch(setProjectBoardFetch(projectBoard.id));
+ }
+ showNotification({show: true, text: 'Задача создана', type: 'success'})
}
});
}
@@ -204,10 +213,14 @@ export const TrackerModal = ({
status: 19,
},
}).then((res) => {
- const result = { ...res, columns: [] };
- dispatch(setProject(result));
- setActive(false);
- setNameProject("");
+ if (!Array.isArray(res.name)) {
+ const result = { ...res, columns: [] };
+ dispatch(setProject(result));
+ setActive(false);
+ setNameProject("");
+ } else {
+ showNotification({show: true, text: 'Проект с таким именем уже существует', type: 'error'})
+ }
});
}
}
diff --git a/src/components/Notification/Notification.jsx b/src/components/Notification/Notification.jsx
new file mode 100644
index 00000000..be463075
--- /dev/null
+++ b/src/components/Notification/Notification.jsx
@@ -0,0 +1,36 @@
+import React from "react";
+import {useDispatch, useSelector} from "react-redux";
+
+import { closeNotification, getNotification } from "@redux/outstaffingSlice";
+
+import close from "assets/icons/closeProjectPersons.svg";
+import copy from "assets/icons/copyNotification.svg";
+import error from "assets/icons/errorNotification.svg";
+import archive from "assets/icons/archiveNotification.svg";
+import success from "assets/icons/successNotification.svg"
+
+const images = {
+ archive: archive,
+ error: error,
+ copy: copy,
+ success: success
+}
+
+import './notification.scss'
+
+export const Notification = () => {
+ const dispatch = useDispatch();
+ const notificationInfo = useSelector(getNotification)
+ return (
+
+
+
+
{notificationInfo.text}
+
+
dispatch(closeNotification())}
+ className='notification__close' src={close} alt='close' />
+
+ )
+}
+
+export default Notification
diff --git a/src/components/Notification/notification.scss b/src/components/Notification/notification.scss
new file mode 100644
index 00000000..cab02ae3
--- /dev/null
+++ b/src/components/Notification/notification.scss
@@ -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;
+ }
+}
diff --git a/src/components/ProjectTiket/ProjectTiket.jsx b/src/components/ProjectTiket/ProjectTiket.jsx
index cefb1f1e..91751542 100644
--- a/src/components/ProjectTiket/ProjectTiket.jsx
+++ b/src/components/ProjectTiket/ProjectTiket.jsx
@@ -3,11 +3,13 @@ import { useDispatch } from "react-redux";
import { Link } from "react-router-dom";
import { deleteProject, modalToggle } from "@redux/projectsTrackerSlice";
+import {useNotification} from "@hooks/useNotification";
import { apiRequest } from "@api/request";
import { ModalSelect } from "@components/Modal/ModalSelect/ModalSelect";
import TrackerModal from "@components/Modal/Tracker/TrackerModal/TrackerModal";
+import AcceptModal from "@components/Modal/AcceptModal/AcceptModal"
import archiveSet from "assets/icons/archive.svg";
import del from "assets/icons/delete.svg";
@@ -19,7 +21,9 @@ import "./projectTiket.scss";
export const ProjectTiket = ({ project, index }) => {
const [modalSelect, setModalSelect] = useState(false);
const [modalAdd, setModalAdd] = useState(false);
+ const [acceptModalOpen, setAcceptModalOpen] = useState(false);
const dispatch = useDispatch();
+ const { showNotification } = useNotification()
useEffect(() => {
initListeners();
@@ -49,6 +53,7 @@ export const ProjectTiket = ({ project, index }) => {
},
}).then(() => {
dispatch(deleteProject(project));
+ showNotification({show: true, text: 'Проект успешно была перемещена в архив', type: 'archive'});
});
}
@@ -58,6 +63,10 @@ export const ProjectTiket = ({ project, index }) => {
);
}
+ function closeAcceptModal () {
+ setAcceptModalOpen(false)
+ }
+
return (
{project.name}
@@ -98,7 +107,10 @@ export const ProjectTiket = ({ project, index }) => {
ссылка на проект
-
+
{
+ setModalSelect(false)
+ setAcceptModalOpen(true)
+ }}>
в архив
@@ -108,6 +120,12 @@ export const ProjectTiket = ({ project, index }) => {
+ {acceptModalOpen &&
+
+ }
);
};
diff --git a/src/hooks/useNotification.js b/src/hooks/useNotification.js
new file mode 100644
index 00000000..7a5a3baf
--- /dev/null
+++ b/src/hooks/useNotification.js
@@ -0,0 +1,15 @@
+import { useDispatch } from "react-redux";
+
+import { setNotification, closeNotification } from "../redux/outstaffingSlice";
+
+export const useNotification = () => {
+ const dispatch = useDispatch();
+
+ const showNotification = (notification) => {
+ dispatch(setNotification(notification))
+ setTimeout(() => {
+ dispatch(closeNotification())
+ }, 2500)
+ }
+ return { showNotification }
+};
diff --git a/src/pages/ProjectTracker/ProjectTracker.js b/src/pages/ProjectTracker/ProjectTracker.js
index f9094701..1cd2a689 100644
--- a/src/pages/ProjectTracker/ProjectTracker.js
+++ b/src/pages/ProjectTracker/ProjectTracker.js
@@ -49,6 +49,7 @@ import archive from "assets/images/archiveIcon.png";
import avatarMok from "assets/images/avatarMok.png";
import { getCorrectDate } from "../../components/Calendar/calendarHelper";
+import {useNotification} from "@hooks/useNotification";
export const ProjectTracker = () => {
const dispatch = useDispatch();
@@ -72,6 +73,7 @@ export const ProjectTracker = () => {
const startWrapperIndexTest = useRef({});
const projectBoard = useSelector(getProjectBoard);
const loader = useSelector(getBoarderLoader);
+ const { showNotification } = useNotification()
useEffect(() => {
dispatch(activeLoader());
@@ -224,6 +226,7 @@ export const ProjectTracker = () => {
} else {
dispatch(setProjectBoardFetch(projectBoard.id));
}
+ showNotification({show: true, text: 'Колонка удалена', type: 'error'})
});
}
diff --git a/src/redux/outstaffingSlice.js b/src/redux/outstaffingSlice.js
index f6f60f69..0f2d9279 100644
--- a/src/redux/outstaffingSlice.js
+++ b/src/redux/outstaffingSlice.js
@@ -1,4 +1,4 @@
-import { createSlice } from "@reduxjs/toolkit";
+import {createAsyncThunk, createSlice} from "@reduxjs/toolkit";
const initialState = {
tags: [],
@@ -14,6 +14,11 @@ const initialState = {
partnerRequestId: null,
partnerRequests: [],
partnerRequestInfo: {},
+ notification: {
+ show: false,
+ text: '',
+ type: ''
+ }
};
export const outstaffingSlice = createSlice({
@@ -62,6 +67,12 @@ export const outstaffingSlice = createSlice({
setPartnerRequestInfo: (state, action) => {
state.partnerRequestInfo = action.payload;
},
+ setNotification: (state, action) => {
+ state.notification = action.payload
+ },
+ closeNotification: (state) => {
+ state.notification.show = false
+ }
},
});
@@ -80,12 +91,16 @@ export const {
setPartnerRequestId,
setPartnerRequests,
setPartnerRequestInfo,
+ setNotification,
+ closeNotification
} = outstaffingSlice.actions;
export const selectProfiles = (state) => state.outstaffing.profiles;
export const selectTags = (state) => state.outstaffing.tags;
export const selectFilteredCandidates = (state) =>
state.outstaffing.filteredCandidates;
+export const getNotification = (state) =>
+ state.outstaffing.notification
export const selectItems = (state) => state.outstaffing.selectedItems;
export const selectCurrentCandidate = (state) =>
state.outstaffing.currentCandidate;