Compare commits
41 Commits
066cd569d3
...
current-de
Author | SHA1 | Date | |
---|---|---|---|
16a5a1f548 | |||
a763c5a69a | |||
a7c58b2609 | |||
3b8ec8e100 | |||
94136c97e6 | |||
4810412bd7 | |||
3f46d60720 | |||
d49a2eb0c8 | |||
8de343016f | |||
470bf6ec67 | |||
dea1eb6104 | |||
ee20b49993 | |||
0e04e19c1b | |||
bd4bfacd66 | |||
1bdabd32bf | |||
2d861de330 | |||
60aa8c7301 | |||
13df697614 | |||
79299d1177 | |||
68a2df23c4 | |||
291e6a4a5d | |||
0d436e71e4 | |||
7a55188904 | |||
3e80159c44 | |||
363d43e04c | |||
a1da184bb0 | |||
adf6ff4a1d | |||
9ffb981b67 | |||
0860591d28 | |||
4a20d5e4fa | |||
1434792d42 | |||
ade31b767a | |||
fafab29d25 | |||
10ffb1f7f1 | |||
75cf13d945 | |||
a3e39b75d8 | |||
c532302f73 | |||
36bdaf15e5 | |||
9f98a0fea2 | |||
1e950e9de0 | |||
3580e25457 |
BIN
src/assets/icons/arrows/arrowReviewsLeft.png
Normal file
After Width: | Height: | Size: 298 B |
BIN
src/assets/icons/arrows/arrowReviewsRight.png
Normal file
After Width: | Height: | Size: 296 B |
3
src/assets/icons/arrows/tableArrow.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="9" height="6" viewBox="0 0 9 6" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.11629 5.8584L8.07227 0.858398H0.160323L4.11629 5.8584Z" fill="#2E3A59"/>
|
||||
</svg>
|
After Width: | Height: | Size: 184 B |
BIN
src/assets/icons/financeIcon.png
Normal file
After Width: | Height: | Size: 726 B |
BIN
src/assets/images/backgroundOpportunity.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
src/assets/images/landingBackgroundCode1.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
src/assets/images/reviewsImgMok.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
src/assets/images/stackProjectsFlag.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
src/assets/images/stackProjectsFly.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
src/assets/images/stackProjectsHand.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
src/assets/images/stackProjectsRabota.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
src/assets/images/stackSteptsPortfolio.png
Normal file
After Width: | Height: | Size: 24 KiB |
@ -299,7 +299,7 @@
|
||||
}
|
||||
|
||||
.pass {
|
||||
background-color: #f7d7c9 !important;
|
||||
background-color: #ba5c33 !important;
|
||||
}
|
||||
|
||||
.today {
|
||||
@ -308,7 +308,7 @@
|
||||
}
|
||||
|
||||
.selected {
|
||||
background-color: #f9f9c3 !important;
|
||||
background-color: #cbcbb4 !important;
|
||||
}
|
||||
|
||||
.block {
|
||||
|
@ -32,7 +32,7 @@ export const Candidate = () => {
|
||||
if (localStorage.getItem("role_status") !== "18") {
|
||||
return <Navigate to="/profile" replace />;
|
||||
}
|
||||
// const { id: candidateId } = useParams();
|
||||
const { id: candidateId } = useParams();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
@ -47,7 +47,7 @@ export const Candidate = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
apiRequest(`/user/me`, {}).then((el) =>
|
||||
apiRequest(`/resume?userId=${candidateId}`).then((el) =>
|
||||
dispatch(currentCandidate(el.userCard))
|
||||
);
|
||||
}, [dispatch]);
|
||||
@ -103,9 +103,7 @@ export const Candidate = () => {
|
||||
link: "/profile/catalog"
|
||||
},
|
||||
{
|
||||
name: `${currentCandidateObj.specification} ${
|
||||
SKILLS[currentCandidateObj.position_id]
|
||||
}, ${LEVELS[currentCandidateObj.level]}`,
|
||||
name: `${currentCandidateObj.fio}`,
|
||||
link: `/candidate/${currentCandidateObj.id}`
|
||||
}
|
||||
]}
|
||||
@ -115,6 +113,7 @@ export const Candidate = () => {
|
||||
<div className="col-12 candidate__header">
|
||||
<div className="candidate__header__left">
|
||||
<h3>
|
||||
{currentCandidateObj.fio} {" "}
|
||||
{currentCandidateObj.specification} {" "}
|
||||
{SKILLS[currentCandidateObj.position_id]} {" "}
|
||||
{LEVELS[currentCandidateObj.level]}
|
||||
|
@ -10,7 +10,7 @@ export const CardControl = ({ title, path, description, img }) => {
|
||||
<Link to={`/${path}`} className="control-card">
|
||||
<div className="control-card__about">
|
||||
<img src={img} alt="itemImg" />
|
||||
<h3>{title}</h3>
|
||||
<h3 dangerouslySetInnerHTML={{ __html: title }}></h3>
|
||||
</div>
|
||||
<div className="control-card__info">
|
||||
<p dangerouslySetInnerHTML={{ __html: description }}></p>
|
||||
|
@ -38,7 +38,7 @@
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
max-width: 125px;
|
||||
max-width: 165px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
|
||||
import { useFormValidation } from "@hooks/useFormValidation";
|
||||
import { useNotification } from "@hooks/useNotification";
|
||||
@ -17,6 +18,7 @@ import "./modalRegistration.scss";
|
||||
export const ModalRegistration = ({ active, setActive }) => {
|
||||
const [loader, setLoader] = useState(false);
|
||||
const [isPartner, setIsPartner] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const fields = {
|
||||
username: "",
|
||||
@ -145,6 +147,7 @@ export const ModalRegistration = ({ active, setActive }) => {
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
await handleSubmit(e);
|
||||
navigate("/welcome-page");
|
||||
}}
|
||||
styles="button-box__submit"
|
||||
>
|
||||
|
@ -664,11 +664,8 @@ export const ModalTiсket = ({
|
||||
"EasyImage",
|
||||
"Image",
|
||||
"ImageCaption",
|
||||
"ImageStyle",
|
||||
"ImageToolbar",
|
||||
"ImageUpload",
|
||||
"MediaEmbed",
|
||||
"BlockQuote"
|
||||
"MediaEmbed"
|
||||
]
|
||||
}}
|
||||
onChange={(event, editor) => {
|
||||
|
@ -683,11 +683,8 @@ export const TicketFullScreen = () => {
|
||||
"EasyImage",
|
||||
"Image",
|
||||
"ImageCaption",
|
||||
"ImageStyle",
|
||||
"ImageToolbar",
|
||||
"ImageUpload",
|
||||
"MediaEmbed",
|
||||
"BlockQuote"
|
||||
"MediaEmbed"
|
||||
]
|
||||
}}
|
||||
onChange={(event, editor) => {
|
||||
|
@ -624,7 +624,15 @@ export const TrackerModal = ({
|
||||
"bulletedList",
|
||||
"numberedList"
|
||||
],
|
||||
removePlugins: ["BlockQuote"],
|
||||
removePlugins: [
|
||||
"CKFinderUploadAdapter",
|
||||
"CKFinder",
|
||||
"EasyImage",
|
||||
"Image",
|
||||
"ImageCaption",
|
||||
"ImageUpload",
|
||||
"MediaEmbed"
|
||||
],
|
||||
placeholder: "Описание задачи"
|
||||
}}
|
||||
onChange={(event, editor) => {
|
||||
|
@ -719,7 +719,6 @@
|
||||
|
||||
.button-add {
|
||||
margin: 0 30px;
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@ import {
|
||||
import {
|
||||
calendarHelper,
|
||||
correctDay,
|
||||
currentMonthAndDay,
|
||||
getCorrectDate,
|
||||
getReports,
|
||||
hourOfNum
|
||||
@ -29,7 +28,6 @@ import BaseButton from "@components/Common/BaseButton/BaseButton";
|
||||
import arrowLeft from "assets/icons/arrows/arrowCalendar_left.png";
|
||||
import arrowRight from "assets/icons/arrows/arrowCalendar_right.png";
|
||||
import calendarIcon from "assets/icons/calendar.svg";
|
||||
// import close from "assets/icons/closeProjectPersons.svg";
|
||||
import rectangle from "assets/images/rectangle__calendar.png";
|
||||
|
||||
export const ProfileCalendarComponent = React.memo(
|
||||
@ -46,6 +44,7 @@ export const ProfileCalendarComponent = React.memo(
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
// const [l, setL] = useState(1);
|
||||
const [calendar, setCalendar] = useState([]);
|
||||
const [month, setMonth] = useState("");
|
||||
const [endDate, setEndDate] = useState(null);
|
||||
@ -174,9 +173,14 @@ export const ProfileCalendarComponent = React.memo(
|
||||
});
|
||||
}
|
||||
|
||||
// function errorr(TotalRangeHours) {
|
||||
// console.error(TotalRangeHours);
|
||||
// }
|
||||
const countHours = (day) => {
|
||||
let hours =
|
||||
reports
|
||||
.find((item) => moment(item.created_at).isSame(day, "date"))
|
||||
?.task.reduce((acc, task) => acc + task.hours_spent, 0) || 0;
|
||||
return `${hours} ${hourOfNum(hours)}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="calendar-component">
|
||||
<div className="calendar-component__header">
|
||||
@ -265,10 +269,9 @@ export const ProfileCalendarComponent = React.memo(
|
||||
<div className="form-date">{day.format("D")}</div>
|
||||
<div className="form-box">
|
||||
<div className="form-hours">
|
||||
<span>7/Час</span>
|
||||
<span>{countHours(day)}</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* {currentMonthAndDay(day)} */}
|
||||
</Link>
|
||||
</button>
|
||||
))
|
||||
|
@ -34,11 +34,15 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
grid-column-gap: 30px;
|
||||
column-gap: 30px;
|
||||
column-gap: 10px;
|
||||
margin-top: 20px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
|
||||
img {
|
||||
width: 23px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
font-size: 14px;
|
||||
|
193
src/components/TrackerCardTask/TrackerCardTask.jsx
Normal file
@ -0,0 +1,193 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
import { movePositionProjectTask } from "@redux/projectsTrackerSlice";
|
||||
|
||||
import { getCorrectDate } from "@utils/calendarHelper";
|
||||
import { removeLast, urlForLocal } from "@utils/helper";
|
||||
|
||||
import TrackerSelectColumn from "@components/TrackerSelectColumn/TrackerSelectColumn";
|
||||
|
||||
import commentsBoard from "assets/icons/commentsBoard.svg";
|
||||
import filesBoard from "assets/icons/filesBoard.svg";
|
||||
import avatarMok from "assets/images/avatarMok.png";
|
||||
|
||||
import "./trackerCardTask.scss";
|
||||
|
||||
const TrackerCardTask = ({
|
||||
task,
|
||||
projectBoard,
|
||||
titleColor,
|
||||
column,
|
||||
openTicket,
|
||||
startWrapperIndexTest,
|
||||
setWrapperHover
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const [taskHover, setTaskHover] = useState({});
|
||||
|
||||
const priority = {
|
||||
2: "Высокий",
|
||||
1: "Средний",
|
||||
0: "Низкий"
|
||||
};
|
||||
|
||||
const priorityClass = {
|
||||
2: "high",
|
||||
1: "middle",
|
||||
0: "low"
|
||||
};
|
||||
|
||||
function dragDropTaskHandler(e, task, column) {
|
||||
e.preventDefault();
|
||||
if (task.id === startWrapperIndexTest.current.task.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const finishTask = column.tasks.indexOf(task);
|
||||
|
||||
dispatch(
|
||||
movePositionProjectTask({
|
||||
startTask: startWrapperIndexTest.current.task,
|
||||
finishTask: task,
|
||||
finishIndex: finishTask
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function dragOverTaskHandler(e, task) {
|
||||
e.preventDefault();
|
||||
if (startWrapperIndexTest.current.task.id === task.id) {
|
||||
return;
|
||||
}
|
||||
setTaskHover((prevState) => ({ [prevState]: false, [task.id]: true }));
|
||||
}
|
||||
|
||||
function dragStartHandler(e, task, columnId) {
|
||||
startWrapperIndexTest.current = { task: task, index: columnId };
|
||||
}
|
||||
|
||||
function dragLeaveTaskHandler() {
|
||||
setTaskHover((prevState) => ({ [prevState]: false }));
|
||||
}
|
||||
|
||||
function dragEndTaskHandler() {
|
||||
setTaskHover((prevState) => ({ [prevState]: false }));
|
||||
setWrapperHover((prevState) => ({
|
||||
[prevState]: false
|
||||
}));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const tasksHover = {};
|
||||
const columnHover = {};
|
||||
|
||||
if (Object.keys(projectBoard).length) {
|
||||
projectBoard.columns.forEach((column) => {
|
||||
columnHover[column.id] = false;
|
||||
column.tasks.forEach((task) => (tasksHover[task.id] = false));
|
||||
});
|
||||
}
|
||||
setWrapperHover(columnHover);
|
||||
setTaskHover(tasksHover);
|
||||
}, [projectBoard]);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={task.id}
|
||||
className={`tasks__board__item ${
|
||||
taskHover[task.id] ? "task__hover" : ""
|
||||
}`}
|
||||
draggable={true}
|
||||
onDragStart={(e) => dragStartHandler(e, task, column.id)}
|
||||
onDragOver={(e) => dragOverTaskHandler(e, task)}
|
||||
onDragLeave={(e) => dragLeaveTaskHandler(e)}
|
||||
onDragEnd={() => dragEndTaskHandler()}
|
||||
onDrop={(e) => dragDropTaskHandler(e, task, column)}
|
||||
onClick={(e) => openTicket(e, task)}
|
||||
>
|
||||
<div
|
||||
className="tasks__board__item__title"
|
||||
onClick={() => {
|
||||
if (window.innerWidth < 985) {
|
||||
window.location.replace(`/tracker/task/${task.id}`);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<p className="task__board__item__title">{task.title}</p>
|
||||
</div>
|
||||
<p
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: task.description
|
||||
}}
|
||||
className="tasks__board__item__description"
|
||||
></p>
|
||||
{Boolean(task.mark.length) && (
|
||||
<div className="tasks__board__item__tags">
|
||||
{task.mark.map((tag) => {
|
||||
return (
|
||||
<div
|
||||
className="tag-item"
|
||||
key={tag.id}
|
||||
style={{ background: tag.color }}
|
||||
>
|
||||
<p>{tag.slug}</p>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<div className="tasks__board__item__container">
|
||||
{typeof task.execution_priority === "number" && (
|
||||
<div className="tasks__board__item__priority">
|
||||
<p>⚡</p>
|
||||
<span className={priorityClass[task.execution_priority]}>
|
||||
{priority[task.execution_priority]}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{task.dead_line && (
|
||||
<div className="tasks__board__item__dead-line">
|
||||
<p>⌛</p>
|
||||
<span style={{ color: titleColor }}>
|
||||
{getCorrectDate(task.dead_line)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="tasks__board__item__info">
|
||||
<div className="tasks__board__item__executor">
|
||||
<img
|
||||
src={
|
||||
task.executor?.avatar
|
||||
? urlForLocal(task.executor?.avatar)
|
||||
: avatarMok
|
||||
}
|
||||
alt="avatar"
|
||||
/>
|
||||
<span>
|
||||
{removeLast(task.executor?.fio) || "Исполнитель не назначен"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="tasks__board__item__info__tags">
|
||||
<div className="tasks__board__item__info__more">
|
||||
<img src={commentsBoard} alt="commentsImg" />
|
||||
<span>{task.comment_count}</span>
|
||||
</div>
|
||||
<div className="tasks__board__item__info__more">
|
||||
<img src={filesBoard} alt="filesImg" />
|
||||
<span>{task.file_count}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<TrackerSelectColumn
|
||||
columns={projectBoard.columns.filter((item) => item.id !== column.id)}
|
||||
currentColumn={column}
|
||||
task={task}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TrackerCardTask;
|
365
src/components/TrackerCardTask/trackerCardTask.scss
Normal file
@ -0,0 +1,365 @@
|
||||
.tasks {
|
||||
&__board {
|
||||
background: #f5f7f9;
|
||||
box-shadow: 0px 2px 5px rgba(60, 66, 87, 0.04),
|
||||
0px 0px 0px 1px rgba(60, 66, 87, 0.08), 0px 1px 1px rgba(0, 0, 0, 0.06);
|
||||
border-radius: 8px;
|
||||
padding: 12px 10px 12px 8px;
|
||||
width: 360px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 10px;
|
||||
height: fit-content;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
transform: scaleY(-1);
|
||||
min-height: 815px;
|
||||
|
||||
@media (max-width: 900px) {
|
||||
min-width: auto;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
transform: scaleX(1);
|
||||
}
|
||||
|
||||
.tasks-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 8px;
|
||||
max-height: 750px;
|
||||
overflow: auto;
|
||||
padding: 5px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #cbd9f9;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #c5c0c6;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__hover {
|
||||
box-shadow: 0px 2px 10px #9cc480, 0px 0px 0px 1px rgba(60, 66, 87, 0.08),
|
||||
0px 1px 1px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.task__hover {
|
||||
box-shadow: 0 0 5px gray;
|
||||
}
|
||||
|
||||
&__item {
|
||||
width: 328px;
|
||||
padding: 6px 10px 8px 10px;
|
||||
position: relative;
|
||||
box-shadow: 0px 3px 2px -2px rgba(0, 0, 0, 0.06),
|
||||
0px 5px 3px -2px rgba(0, 0, 0, 0.02);
|
||||
border-radius: 6px;
|
||||
background: #ffffff;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
transition: 0.4s;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.025);
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
width: 100%;
|
||||
max-height: none;
|
||||
|
||||
&:hover {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__hide {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&__title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
|
||||
p {
|
||||
color: #1a1919;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 0;
|
||||
max-height: 100px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 3;
|
||||
}
|
||||
|
||||
span {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
border-radius: 6px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
padding-bottom: 10px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 1px solid #dddddd;
|
||||
}
|
||||
}
|
||||
|
||||
&__description {
|
||||
margin: 4px 0;
|
||||
color: #5c6165;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 120%;
|
||||
max-height: 100px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
&__container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
column-gap: 10px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
pointer-events: none;
|
||||
margin-top: 5px;
|
||||
|
||||
&__tags {
|
||||
display: flex;
|
||||
column-gap: 5px;
|
||||
}
|
||||
|
||||
&__more {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
span {
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
color: #6e7c87;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
&__avatars {
|
||||
position: relative;
|
||||
img {
|
||||
position: relative;
|
||||
}
|
||||
img:first-child {
|
||||
right: -15px;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__priority {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 5px;
|
||||
margin-top: 3px;
|
||||
|
||||
p {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.high {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.middle {
|
||||
color: #cece00;
|
||||
}
|
||||
|
||||
.low {
|
||||
color: green;
|
||||
}
|
||||
}
|
||||
|
||||
&__dead-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 5px;
|
||||
|
||||
p {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: #1458dd;
|
||||
}
|
||||
|
||||
span {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
img {
|
||||
margin-top: -2px;
|
||||
width: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
&__executor {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
column-gap: 5px;
|
||||
|
||||
span {
|
||||
max-width: 210px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
&__tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 6px;
|
||||
row-gap: 3px;
|
||||
margin: 3px 0;
|
||||
|
||||
.tag-item {
|
||||
padding: 3px 10px;
|
||||
border-radius: 10px;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.open-items {
|
||||
cursor: pointer;
|
||||
border-radius: 44px;
|
||||
width: 33px;
|
||||
height: 33px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
bottom: -15px;
|
||||
font-size: 20px;
|
||||
left: 165px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.more-items {
|
||||
background: #8bcc60;
|
||||
}
|
||||
|
||||
.less-items {
|
||||
background: #f92828;
|
||||
}
|
||||
|
||||
&__more {
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
.column__select {
|
||||
position: absolute;
|
||||
padding: 15px;
|
||||
background: #e1fccf;
|
||||
border-radius: 12px;
|
||||
right: -20px;
|
||||
top: 5px;
|
||||
z-index: 7;
|
||||
row-gap: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@media (max-width: 910px) {
|
||||
right: 10px;
|
||||
top: 40px;
|
||||
}
|
||||
|
||||
&__item {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
img {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__no-items {
|
||||
font-weight: 500;
|
||||
font-size: 25px;
|
||||
transform: scaleY(-1);
|
||||
|
||||
@media (max-width: 900px) {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__no-tasks {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transform: scaleY(-1);
|
||||
|
||||
&-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
img {
|
||||
width: 27px;
|
||||
height: 27px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 700;
|
||||
font-size: 22px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
&-more {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
113
src/components/TrackerSelectExecutor/TrackerSelectExecutor.jsx
Normal file
@ -0,0 +1,113 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
import { filteredExecutorTasks } from "@redux/projectsTrackerSlice";
|
||||
|
||||
import { removeLast, urlForLocal } from "@utils/helper";
|
||||
|
||||
import arrowDown from "assets/icons/arrows/selectArrow.png";
|
||||
import close from "assets/icons/close.png";
|
||||
import avatarMok from "assets/images/avatarMok.png";
|
||||
|
||||
import "./trackerSelectExecutor.scss";
|
||||
|
||||
const TrackerSelectExecutor = ({
|
||||
selectedExecutor,
|
||||
setSelectedExecutor,
|
||||
deleteSelectedExecutor,
|
||||
projectBoard
|
||||
}) => {
|
||||
const [selectExecutorOpen, setSelectedExecutorOpen] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const initListeners = () => {
|
||||
document.addEventListener("click", closeByClickingOut);
|
||||
};
|
||||
|
||||
const closeByClickingOut = (event) => {
|
||||
const path = event.path || (event.composedPath && event.composedPath());
|
||||
|
||||
if (
|
||||
event &&
|
||||
!path.find(
|
||||
(div) =>
|
||||
div.classList &&
|
||||
(div.classList.contains("tasks__head__executor") ||
|
||||
div.classList.contains("tasks__head__executor-dropdown"))
|
||||
)
|
||||
) {
|
||||
setSelectedExecutorOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
function executorFilter(user) {
|
||||
dispatch(filteredExecutorTasks(user.user_id));
|
||||
setSelectedExecutor(user);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
initListeners();
|
||||
}, []);
|
||||
|
||||
if (selectedExecutor) {
|
||||
return (
|
||||
<div className="tasks__head__executor-selected">
|
||||
<p>{removeLast(selectedExecutor.user.fio)}</p>
|
||||
<img
|
||||
className="avatar"
|
||||
src={
|
||||
selectedExecutor.user?.avatar
|
||||
? urlForLocal(selectedExecutor.user.avatar)
|
||||
: avatarMok
|
||||
}
|
||||
alt="avatar"
|
||||
/>
|
||||
<img
|
||||
className="delete"
|
||||
src={close}
|
||||
alt="delete"
|
||||
onClick={deleteSelectedExecutor}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div
|
||||
className="tasks__head__executor"
|
||||
onClick={() => setSelectedExecutorOpen(!selectExecutorOpen)}
|
||||
>
|
||||
<p>Выберите исполнителя</p>
|
||||
<img
|
||||
className={selectExecutorOpen ? "open" : ""}
|
||||
src={arrowDown}
|
||||
alt="arrow"
|
||||
/>
|
||||
{selectExecutorOpen && (
|
||||
<div className="tasks__head__executor-dropdown">
|
||||
{projectBoard.projectUsers.map((user) => {
|
||||
return (
|
||||
<div
|
||||
className="executor-dropdown__person"
|
||||
key={user.user_id}
|
||||
onClick={() => executorFilter(user)}
|
||||
>
|
||||
<p>{removeLast(user.user?.fio)}</p>
|
||||
<img
|
||||
src={
|
||||
user.user?.avatar
|
||||
? urlForLocal(user.user.avatar)
|
||||
: avatarMok
|
||||
}
|
||||
alt="avatar"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default TrackerSelectExecutor;
|
129
src/components/TrackerSelectExecutor/trackerSelectExecutor.scss
Normal file
@ -0,0 +1,129 @@
|
||||
.tasks {
|
||||
&__head {
|
||||
&__executor {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e3e2e2;
|
||||
padding: 2px 6px;
|
||||
position: relative;
|
||||
max-width: 190px;
|
||||
width: 100%;
|
||||
|
||||
@media (max-width: 915px) {
|
||||
margin-right: 0;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
@media (max-width: 650px) {
|
||||
border-color: gray;
|
||||
}
|
||||
|
||||
&-selected {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 8px;
|
||||
max-width: 220px;
|
||||
width: 100%;
|
||||
margin-right: 10px;
|
||||
justify-content: center;
|
||||
|
||||
p {
|
||||
color: #252c32;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
max-width: 155px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.delete {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
img {
|
||||
display: flex;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 915px) {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
justify-content: start;
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
p {
|
||||
color: #252c32;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
img {
|
||||
transition: all 0.15s ease;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
&-dropdown {
|
||||
position: absolute;
|
||||
top: 33px;
|
||||
left: 0;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
z-index: 5;
|
||||
padding: 10px 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 7px;
|
||||
width: 100%;
|
||||
|
||||
.executor-dropdown__person {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
p {
|
||||
max-width: 155px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@media (max-width: 915px) {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
p {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
275
src/components/TrackerTagList/TrackerTagList.jsx
Normal file
@ -0,0 +1,275 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { HexColorPicker } from "react-colorful";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import {
|
||||
addNewTagToProject,
|
||||
deleteTagProject,
|
||||
setProjectBoardFetch
|
||||
} from "@redux/projectsTrackerSlice";
|
||||
|
||||
import { apiRequest } from "@api/request";
|
||||
|
||||
import { useNotification } from "@hooks/useNotification";
|
||||
|
||||
import arrowDown from "assets/icons/arrows/selectArrow.png";
|
||||
import close from "assets/icons/close.png";
|
||||
import edit from "assets/icons/edit.svg";
|
||||
|
||||
import "./trackerTagList.scss";
|
||||
|
||||
const TrackerTagList = ({ projectBoard }) => {
|
||||
const dispatch = useDispatch();
|
||||
const projectId = useParams();
|
||||
|
||||
const { showNotification } = useNotification();
|
||||
const [tagInfo, setTagInfo] = useState({ description: "", name: "" });
|
||||
const [color, setColor] = useState("#aabbcc");
|
||||
const [tags, setTags] = useState({
|
||||
open: false,
|
||||
add: false,
|
||||
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));
|
||||
showNotification({
|
||||
show: true,
|
||||
text: "Тег удален",
|
||||
type: "success"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addNewTag() {
|
||||
apiRequest("/mark/create", {
|
||||
method: "POST",
|
||||
data: {
|
||||
title: tagInfo.description,
|
||||
slug: tagInfo.name,
|
||||
color: color,
|
||||
status: 1
|
||||
}
|
||||
}).then((data) => {
|
||||
apiRequest("/mark/attach", {
|
||||
method: "POST",
|
||||
data: {
|
||||
mark_id: data.id,
|
||||
entity_type: 1,
|
||||
entity_id: projectId.id
|
||||
}
|
||||
}).then((data) => {
|
||||
dispatch(addNewTagToProject(data.mark));
|
||||
setTags((prevState) => ({
|
||||
...prevState,
|
||||
add: false
|
||||
}));
|
||||
showNotification({
|
||||
show: true,
|
||||
text: "Тег успешно создан",
|
||||
type: "success"
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
}));
|
||||
setTagInfo({ description: "", name: "" });
|
||||
setColor("#aabbcc");
|
||||
showNotification({
|
||||
show: true,
|
||||
text: "Тег успешно изменён",
|
||||
type: "success"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const initListeners = () => {
|
||||
document.addEventListener("click", closeByClickingOut);
|
||||
};
|
||||
|
||||
const closeByClickingOut = (event) => {
|
||||
const path = event.path || (event.composedPath && event.composedPath());
|
||||
if (
|
||||
event &&
|
||||
!path.find(
|
||||
(div) =>
|
||||
div.classList &&
|
||||
(div.classList.contains("tasks__head__tags") ||
|
||||
div.classList.contains("tags__list"))
|
||||
)
|
||||
) {
|
||||
setTags({ open: false, add: false, edit: false });
|
||||
setTagInfo({ description: "", name: "" });
|
||||
setColor("#aabbcc");
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
initListeners();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="tasks__head__tags">
|
||||
<div
|
||||
className="tags__add"
|
||||
onClick={() => {
|
||||
setTags((prevState) => ({
|
||||
...prevState,
|
||||
open: !tags.open
|
||||
}));
|
||||
}}
|
||||
>
|
||||
<p>Список тегов</p>
|
||||
<img className={tags.open ? "open" : ""} src={arrowDown} alt="arrow" />
|
||||
</div>
|
||||
{tags.open && (
|
||||
<div className="tags__list">
|
||||
{!tags.add && !tags.edit && (
|
||||
<div
|
||||
className="add-new-tag"
|
||||
onClick={() =>
|
||||
setTags((prevState) => ({
|
||||
...prevState,
|
||||
add: true
|
||||
}))
|
||||
}
|
||||
>
|
||||
<p>Добавить новый тег</p>
|
||||
<span>+</span>
|
||||
</div>
|
||||
)}
|
||||
{!tags.add && !tags.edit && (
|
||||
<div className="tags__list__created">
|
||||
{projectBoard.mark.map((tag) => {
|
||||
return (
|
||||
<div className="tag-item" key={tag.id}>
|
||||
<div className="tag-item__info">
|
||||
<span
|
||||
className="tag-item__color"
|
||||
style={{ background: tag.color }}
|
||||
/>
|
||||
<div>
|
||||
<span className="tag-item__info__name">{tag.slug}</span>
|
||||
<p className="tag-item__description">{tag.title}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="tag-item__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>
|
||||
)}
|
||||
{(tags.add || tags.edit) && (
|
||||
<div className="form-tag">
|
||||
<input
|
||||
className="form-tag__input"
|
||||
placeholder="Описание метки"
|
||||
maxLength="25"
|
||||
value={tagInfo.description}
|
||||
onChange={(e) =>
|
||||
setTagInfo((prevState) => ({
|
||||
...prevState,
|
||||
description: e.target.value
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<input
|
||||
className="form-tag__input"
|
||||
placeholder="Тег"
|
||||
value={tagInfo.name}
|
||||
maxLength="10"
|
||||
onChange={(e) =>
|
||||
setTagInfo((prevState) => ({
|
||||
...prevState,
|
||||
name: e.target.value
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<HexColorPicker color={color} onChange={setColor} />
|
||||
<button
|
||||
onClick={() => {
|
||||
tags.add ? addNewTag() : editTag();
|
||||
}}
|
||||
className={
|
||||
tagInfo.name && tagInfo.description
|
||||
? "form-tag__btn"
|
||||
: "form-tag__btn disable"
|
||||
}
|
||||
>
|
||||
{tags.add ? "Добавить" : "Изменить"}
|
||||
</button>
|
||||
{(tags.add || tags.edit) && (
|
||||
<button
|
||||
className={"form-tag__btn"}
|
||||
onClick={() => {
|
||||
setTags((prevState) => ({
|
||||
...prevState,
|
||||
add: false,
|
||||
edit: false
|
||||
}));
|
||||
setTagInfo({
|
||||
description: "",
|
||||
name: ""
|
||||
});
|
||||
setColor("#aabbcc");
|
||||
}}
|
||||
>
|
||||
Отмена
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TrackerTagList;
|
208
src/components/TrackerTagList/trackerTagList.scss
Normal file
@ -0,0 +1,208 @@
|
||||
.tasks {
|
||||
&__head {
|
||||
&__tags {
|
||||
position: relative;
|
||||
img {
|
||||
transition: all 0.15s ease;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.tags {
|
||||
&__add {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 10px;
|
||||
column-gap: 5px;
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e3e2e2;
|
||||
max-height: 30px;
|
||||
|
||||
p {
|
||||
white-space: nowrap;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
}
|
||||
|
||||
span {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
border-radius: 50px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
background: #99b4f3;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
}
|
||||
&__list {
|
||||
position: absolute;
|
||||
border-radius: 8px;
|
||||
background: #d9d9d9;
|
||||
z-index: 8;
|
||||
top: 30px;
|
||||
left: -35px;
|
||||
width: 216px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@media (max-width: 900px) {
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.close {
|
||||
cursor: pointer;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
&__created {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 8px;
|
||||
margin-top: 8px;
|
||||
padding: 0 8px 8px;
|
||||
|
||||
.tag-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
column-gap: 5px;
|
||||
padding: 0px 8px;
|
||||
border-radius: 8px;
|
||||
height: 40px;
|
||||
max-height: 40px;
|
||||
background: #fff;
|
||||
|
||||
&__description {
|
||||
font-size: 12px;
|
||||
word-break: break-word;
|
||||
max-width: 115px;
|
||||
max-height: 40px;
|
||||
overflow: hidden;
|
||||
text-wrap: wrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&__color {
|
||||
width: 22.25px;
|
||||
height: 23.217px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
&__images {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
row-gap: 3px;
|
||||
|
||||
img {
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.delete {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 10px;
|
||||
|
||||
&__name {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-new-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 15px;
|
||||
border-radius: 8px;
|
||||
background: white;
|
||||
color: #252c32;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
justify-content: center;
|
||||
margin: 8px 8px 0px;
|
||||
|
||||
p {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
span {
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
border-radius: 50px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
background: #52b709;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.form-tag {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 8px;
|
||||
row-gap: 8px;
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 5px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
&__input {
|
||||
outline: none;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e3e2e2;
|
||||
font-size: 15px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
&__btn {
|
||||
outline: none;
|
||||
border: none;
|
||||
background: #252c32;
|
||||
color: whitesmoke;
|
||||
margin: 0 auto 0;
|
||||
border-radius: 10px;
|
||||
font-size: 15px;
|
||||
padding: 5px 12px;
|
||||
}
|
||||
|
||||
.disable {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -127,11 +127,8 @@ export const TrackerTaskComment = ({
|
||||
"EasyImage",
|
||||
"Image",
|
||||
"ImageCaption",
|
||||
"ImageStyle",
|
||||
"ImageToolbar",
|
||||
"ImageUpload",
|
||||
"MediaEmbed",
|
||||
"BlockQuote"
|
||||
"MediaEmbed"
|
||||
]
|
||||
}}
|
||||
onChange={(event, editor) => {
|
||||
|
@ -2,11 +2,11 @@ import React from "react";
|
||||
import SVG from "react-inlinesvg";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { Footer } from "@components/Common/Footer/Footer";
|
||||
|
||||
import arrow from "assets/icons/arrows/arrowLanding.svg";
|
||||
import authIcon from "assets/icons/authIcon.svg";
|
||||
import clue from "assets/icons/landingClue.svg";
|
||||
import telegram from "assets/icons/telegramIcon.svg";
|
||||
import vk from "assets/icons/vkIcon.svg";
|
||||
import codeBg from "assets/images/landingBackgroundCode.svg";
|
||||
import cat from "assets/images/landingCat.png";
|
||||
|
||||
@ -37,16 +37,7 @@ export const Landing = () => {
|
||||
img: arrow
|
||||
}
|
||||
];
|
||||
const socials = [
|
||||
{
|
||||
img: vk,
|
||||
to: ""
|
||||
},
|
||||
{
|
||||
img: telegram,
|
||||
to: ""
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="landing">
|
||||
<div className="landing__container">
|
||||
@ -106,26 +97,7 @@ export const Landing = () => {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="landing__footer">
|
||||
<div className="footer__links">
|
||||
<div className="footer__socials">
|
||||
{socials.map((social, index) => {
|
||||
return (
|
||||
<Link to={social.to} key={index}>
|
||||
<SVG src={social.img} />
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Link className="footer__invite" to="/auth">
|
||||
Присоединиться к команде
|
||||
</Link>
|
||||
</div>
|
||||
<div className="footer__info">
|
||||
<p>office@itguild.info</p>
|
||||
<p>© 2024 - Outstaffing. Все права защищены</p>
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
.landing {
|
||||
background: #EEEEEE;
|
||||
height: 100vh;
|
||||
min-height: 100vh;
|
||||
padding: 20px 0;
|
||||
font-family: "GT Eesti Pro Display";
|
||||
|
||||
@ -151,7 +151,7 @@
|
||||
}
|
||||
|
||||
&__opportunities {
|
||||
margin-top: 120px;
|
||||
margin: 120px 0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
|
||||
}
|
||||
@ -243,46 +243,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
margin-top: auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
&__links {
|
||||
display: flex;
|
||||
column-gap: 23px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__socials {
|
||||
display: flex;
|
||||
column-gap: 16px;
|
||||
}
|
||||
|
||||
&__invite {
|
||||
color: rgba(159, 159, 159, 1);
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
letter-spacing: 0.01em;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
column-gap: 100px;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
letter-spacing: 0.01em;
|
||||
color: rgba(159, 159, 159, 1);
|
||||
|
||||
p:first-child {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,17 +34,20 @@ export const PartnerAddRequest = () => {
|
||||
const [specializationList, setSpecializationList] = useState([]);
|
||||
const [levelList, setLevelList] = useState([]);
|
||||
const [countList] = useState([1, 2, 3, 4, 5]);
|
||||
const [locationList] = useState(["РФ", "Беларусь"]);
|
||||
const [openSkillsSelect, setOpenSkillsSelect] = useState(false);
|
||||
const [openSpecializationList, setOpenSpecializationListOpen] =
|
||||
useState(false);
|
||||
const [openLevelList, setOpenLevelList] = useState(false);
|
||||
const [openCountList, setOpenCountList] = useState(false);
|
||||
const [openLocationList, setOpenLocationList] = useState(false);
|
||||
const [editRequest, setEditRequest] = useState(false);
|
||||
const [selectedSkills, setSelectedSkills] = useState([]);
|
||||
const [selectedSpecialization, setSelectedSpecialization] = useState(
|
||||
"Выберите специализацию"
|
||||
);
|
||||
const [selectedLevel, setSelectedLevel] = useState("Выберите уровень");
|
||||
const [selectedLocation, setSelectedLocation] = useState("Выберите локацию");
|
||||
const [selectedCount, setSelectedCount] = useState(
|
||||
"Выберите кол-во сотрудников"
|
||||
);
|
||||
@ -177,6 +180,7 @@ export const PartnerAddRequest = () => {
|
||||
setOpenSpecializationListOpen(false);
|
||||
setOpenLevelList(false);
|
||||
setOpenCountList(false);
|
||||
setOpenLocationList(false);
|
||||
}
|
||||
};
|
||||
|
||||
@ -202,280 +206,329 @@ export const PartnerAddRequest = () => {
|
||||
? "Страница редактирования заявки"
|
||||
: "Страница добавления заявки"}
|
||||
</h2>
|
||||
<div className="partner-add-request__section">
|
||||
<div className="partner-add-request__form">
|
||||
<div className="partner-add-request__form__block form__block">
|
||||
<h3 className="form__block__title">Данные открытой позиции</h3>
|
||||
<div className="form__block__section">
|
||||
<h3>Название вакансии</h3>
|
||||
<div className="form__block__section__input">
|
||||
<input
|
||||
value={inputs.title}
|
||||
onChange={(e) =>
|
||||
setInputs((prevValue) => ({
|
||||
...prevValue,
|
||||
title: e.target.value
|
||||
}))
|
||||
}
|
||||
type="text"
|
||||
placeholder="Вакансия"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form__block__section">
|
||||
<h3>Выберите специализацию</h3>
|
||||
<div
|
||||
className="form__block__section__selects"
|
||||
onClick={() => {
|
||||
setOpenSpecializationListOpen(!openSpecializationList);
|
||||
}}
|
||||
>
|
||||
<div className="form__block__section__select">
|
||||
<span>
|
||||
{typeof selectedSpecialization === "string"
|
||||
? selectedSpecialization
|
||||
: selectedSpecialization.name}
|
||||
</span>
|
||||
<img
|
||||
className={openSpecializationList ? "rotate" : ""}
|
||||
src={arrowDown}
|
||||
<div className="partner-add-request__main">
|
||||
<div className="partner-add-request__section">
|
||||
<div className="partner-add-request__form">
|
||||
<div className="partner-add-request__form__block form__block">
|
||||
<h3 className="form__block__title">Данные открытой позиции</h3>
|
||||
<div className="form__block__section">
|
||||
<h3>Название вакансии</h3>
|
||||
<div className="form__block__section__input">
|
||||
<input
|
||||
value={inputs.title}
|
||||
onChange={(e) =>
|
||||
setInputs((prevValue) => ({
|
||||
...prevValue,
|
||||
title: e.target.value
|
||||
}))
|
||||
}
|
||||
type="text"
|
||||
placeholder="Вакансия"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{openSpecializationList &&
|
||||
Boolean(specializationList.length) && (
|
||||
<div className="form__block__dropDown">
|
||||
{specializationList.map((specialization) => {
|
||||
<div className="form__block__section">
|
||||
<h3>Выберите специализацию</h3>
|
||||
<div
|
||||
className="form__block__section__selects"
|
||||
onClick={() => {
|
||||
setOpenSpecializationListOpen(!openSpecializationList);
|
||||
}}
|
||||
>
|
||||
<div className="form__block__section__select">
|
||||
<span>
|
||||
{typeof selectedSpecialization === "string"
|
||||
? selectedSpecialization
|
||||
: selectedSpecialization.name}
|
||||
</span>
|
||||
<img
|
||||
className={openSpecializationList ? "rotate" : ""}
|
||||
src={arrowDown}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{openSpecializationList &&
|
||||
Boolean(specializationList.length) && (
|
||||
<div className="form__block__dropDown">
|
||||
{specializationList.map((specialization) => {
|
||||
return (
|
||||
<p
|
||||
key={specialization.id}
|
||||
onClick={() => {
|
||||
setOpenSpecializationListOpen(false);
|
||||
setSelectedSpecialization(specialization);
|
||||
}}
|
||||
>
|
||||
{specialization.name}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="form__block__section">
|
||||
<h3>Навыки</h3>
|
||||
<div
|
||||
className="form__block__skills"
|
||||
onClick={() => {
|
||||
setOpenSkillsSelect(true);
|
||||
}}
|
||||
>
|
||||
{Boolean(selectedSkills.length) &&
|
||||
selectedSkills.map((skill, index) => {
|
||||
return (
|
||||
<p
|
||||
key={specialization.id}
|
||||
<div className="skill" key={`selected-${skill.id}`}>
|
||||
<span>{skill.name}</span>
|
||||
<img
|
||||
src={deleteIcon}
|
||||
alt="delete"
|
||||
onClick={() => {
|
||||
setSkills((prevArray) => [...prevArray, skill]);
|
||||
setFilteredSkills((prevArray) => [
|
||||
...prevArray,
|
||||
skill
|
||||
]);
|
||||
setSelectedSkills(
|
||||
selectedSkills.filter((skill, indexSkill) => {
|
||||
return indexSkill !== index;
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Выберите навыки"
|
||||
onChange={(e) => {
|
||||
setFilteredSkills(
|
||||
skills.filter((skill) => {
|
||||
return skill.name
|
||||
.toLowerCase()
|
||||
.includes(e.target.value.toLowerCase());
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{openSkillsSelect && Boolean(filteredSkills.length) && (
|
||||
<div className="form__block__dropDown">
|
||||
{filteredSkills.map((skill, index) => {
|
||||
return (
|
||||
<span
|
||||
key={skill.id}
|
||||
onClick={() => {
|
||||
setOpenSpecializationListOpen(false);
|
||||
setSelectedSpecialization(specialization);
|
||||
setSelectedSkills((prevArray) => [
|
||||
...prevArray,
|
||||
skill
|
||||
]);
|
||||
setFilteredSkills(
|
||||
filteredSkills.filter((skill, skillIndex) => {
|
||||
return skillIndex !== index;
|
||||
})
|
||||
);
|
||||
setSkills(
|
||||
skills.filter((initSkill) => {
|
||||
return initSkill.id !== skill.id;
|
||||
})
|
||||
);
|
||||
setOpenSkillsSelect(false);
|
||||
}}
|
||||
>
|
||||
{specialization.name}
|
||||
{skill.name}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="partner-add-request__form__block form__block">
|
||||
<h3 className="form__block__title">Квалификация</h3>
|
||||
<div className="form__block__section">
|
||||
<h3>Выберите уровень знаний </h3>
|
||||
<div
|
||||
className="form__block__section__select"
|
||||
onClick={() => setOpenLevelList(!openLevelList)}
|
||||
>
|
||||
<span>
|
||||
{typeof selectedLevel === "string"
|
||||
? selectedLevel
|
||||
: selectedLevel.name}
|
||||
</span>
|
||||
<img
|
||||
className={openLevelList ? "rotate" : ""}
|
||||
src={arrowDown}
|
||||
/>
|
||||
</div>
|
||||
{openLevelList &&
|
||||
Boolean(Object.values(levelList).length) && (
|
||||
<div className="form__block__dropDown">
|
||||
{Object.values(levelList).map((level, index) => {
|
||||
return (
|
||||
<p
|
||||
key={level}
|
||||
onClick={() => {
|
||||
setOpenLevelList(false);
|
||||
setSelectedLevel({
|
||||
name: level,
|
||||
id: index + 1
|
||||
});
|
||||
}}
|
||||
>
|
||||
{level}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="form__block__section">
|
||||
<h3>Введите необходимое описание</h3>
|
||||
<textarea
|
||||
value={inputs.description}
|
||||
onChange={(e) =>
|
||||
setInputs((prevValue) => ({
|
||||
...prevValue,
|
||||
description: e.target.value
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="form__block__section">
|
||||
<h3>Необходимое количество человек на позицию</h3>
|
||||
<div
|
||||
className="form__block__section__select"
|
||||
onClick={() => setOpenCountList(true)}
|
||||
>
|
||||
<span>{selectedCount}</span>
|
||||
<img
|
||||
className={openCountList ? "rotate" : ""}
|
||||
src={arrowDown}
|
||||
/>
|
||||
</div>
|
||||
{openCountList && (
|
||||
<div className="form__block__dropDown">
|
||||
{countList.map((count) => {
|
||||
return (
|
||||
<p
|
||||
key={count}
|
||||
onClick={() => {
|
||||
setOpenCountList(false);
|
||||
setSelectedCount(count);
|
||||
}}
|
||||
>
|
||||
{count}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="form__block__section">
|
||||
<h3>Навыки</h3>
|
||||
<div
|
||||
className="form__block__skills"
|
||||
onClick={() => {
|
||||
setOpenSkillsSelect(true);
|
||||
}}
|
||||
>
|
||||
{Boolean(selectedSkills.length) &&
|
||||
selectedSkills.map((skill, index) => {
|
||||
return (
|
||||
<div className="skill" key={`selected-${skill.id}`}>
|
||||
<span>{skill.name}</span>
|
||||
<img
|
||||
src={deleteIcon}
|
||||
alt="delete"
|
||||
onClick={() => {
|
||||
setSkills((prevArray) => [...prevArray, skill]);
|
||||
setFilteredSkills((prevArray) => [
|
||||
...prevArray,
|
||||
skill
|
||||
]);
|
||||
setSelectedSkills(
|
||||
selectedSkills.filter((skill, indexSkill) => {
|
||||
return indexSkill !== index;
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Выберите навыки"
|
||||
onChange={(e) => {
|
||||
setFilteredSkills(
|
||||
skills.filter((skill) => {
|
||||
return skill.name
|
||||
.toLowerCase()
|
||||
.includes(e.target.value.toLowerCase());
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{openSkillsSelect && Boolean(filteredSkills.length) && (
|
||||
<div className="form__block__dropDown">
|
||||
{filteredSkills.map((skill, index) => {
|
||||
return (
|
||||
<span
|
||||
key={skill.id}
|
||||
onClick={() => {
|
||||
setSelectedSkills((prevArray) => [
|
||||
...prevArray,
|
||||
skill
|
||||
]);
|
||||
setFilteredSkills(
|
||||
filteredSkills.filter((skill, skillIndex) => {
|
||||
return skillIndex !== index;
|
||||
})
|
||||
);
|
||||
setSkills(
|
||||
skills.filter((initSkill) => {
|
||||
return initSkill.id !== skill.id;
|
||||
})
|
||||
);
|
||||
setOpenSkillsSelect(false);
|
||||
}}
|
||||
>
|
||||
{skill.name}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="partner-add-request__form__block form__block">
|
||||
<h3 className="form__block__title">Квалификация</h3>
|
||||
<div className="form__block__section">
|
||||
<h3>Выберите уровень знаний </h3>
|
||||
<div
|
||||
className="form__block__section__select"
|
||||
onClick={() => setOpenLevelList(!openLevelList)}
|
||||
>
|
||||
<span>
|
||||
{typeof selectedLevel === "string"
|
||||
? selectedLevel
|
||||
: selectedLevel.name}
|
||||
</span>
|
||||
<img
|
||||
className={openLevelList ? "rotate" : ""}
|
||||
src={arrowDown}
|
||||
/>
|
||||
<div className="partner-add-request__info">
|
||||
<div className="partner-add-request__info__block">
|
||||
<div className="partner-add-request__info__block__title">
|
||||
<img src={processImg} alt="process" />
|
||||
<h4>Процесс:</h4>
|
||||
</div>
|
||||
{openLevelList && Boolean(Object.values(levelList).length) && (
|
||||
<div className="form__block__dropDown">
|
||||
{Object.values(levelList).map((level, index) => {
|
||||
return (
|
||||
<p
|
||||
key={level}
|
||||
onClick={() => {
|
||||
setOpenLevelList(false);
|
||||
setSelectedLevel({ name: level, id: index + 1 });
|
||||
}}
|
||||
>
|
||||
{level}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<p>
|
||||
При аутстаффе мы предоставляем вам IT-специалистов при этом
|
||||
они находятся в нашем штате.
|
||||
<br />
|
||||
<br />
|
||||
Вы сможете прособеседовать наших специалистов, посмотреть
|
||||
проекты и Git.
|
||||
</p>
|
||||
</div>
|
||||
<div className="form__block__section">
|
||||
<h3>Введите необходимое описание</h3>
|
||||
<textarea
|
||||
value={inputs.description}
|
||||
onChange={(e) =>
|
||||
setInputs((prevValue) => ({
|
||||
...prevValue,
|
||||
description: e.target.value
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="form__block__section">
|
||||
<h3>Необходимое количество человек на позицию</h3>
|
||||
<div
|
||||
className="form__block__section__select"
|
||||
onClick={() => setOpenCountList(true)}
|
||||
>
|
||||
<span>{selectedCount}</span>
|
||||
<img
|
||||
className={openCountList ? "rotate" : ""}
|
||||
src={arrowDown}
|
||||
/>
|
||||
<div className="partner-add-request__info__block">
|
||||
<div className="partner-add-request__info__block__title">
|
||||
<img src={reportImg} alt="reportImg" />
|
||||
<h4>Отчетность:</h4>
|
||||
</div>
|
||||
{openCountList && (
|
||||
<div className="form__block__dropDown">
|
||||
{countList.map((count) => {
|
||||
return (
|
||||
<p
|
||||
key={count}
|
||||
onClick={() => {
|
||||
setOpenCountList(false);
|
||||
setSelectedCount(count);
|
||||
}}
|
||||
>
|
||||
{count}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<p>
|
||||
Вы можете обратиться к специалисту напрямую.
|
||||
<br />
|
||||
<br />
|
||||
Каждый день специалисты описывают выполненные работы и
|
||||
затраченные на это часы.
|
||||
<br />
|
||||
<br />
|
||||
Можем выделить руководителя проекта и тестировщиков.
|
||||
</p>
|
||||
</div>
|
||||
<div className="form__block__buttons">
|
||||
<Link to="/profile/requests" className="form__block__cancel">
|
||||
Отмена
|
||||
</Link>
|
||||
<button
|
||||
onClick={() => handler()}
|
||||
className={
|
||||
disableBtn()
|
||||
? "form__block__save"
|
||||
: "form__block__save disable"
|
||||
}
|
||||
>
|
||||
Сохранить
|
||||
</button>
|
||||
<div className="partner-add-request__info__block">
|
||||
<div className="partner-add-request__info__block__title">
|
||||
<img src={documentsImg} alt="documentsImg" />
|
||||
<h4>
|
||||
Обмен <br />
|
||||
документами:
|
||||
</h4>
|
||||
</div>
|
||||
<p>
|
||||
В Личном кабинете платформы получайте отчеты выполненных работ
|
||||
и счеты на согласование и оплату
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="partner-add-request__info">
|
||||
<div className="partner-add-request__info__block">
|
||||
<div className="partner-add-request__info__block__title">
|
||||
<img src={processImg} alt="process" />
|
||||
<h4>Процесс:</h4>
|
||||
<div className="partner-add-request__special">
|
||||
<h4>Основные требования по вакансии</h4>
|
||||
<div className="form__block__section special__select">
|
||||
<h3>Локация</h3>
|
||||
<div
|
||||
className="form__block__section__select"
|
||||
onClick={() => setOpenLocationList(true)}
|
||||
>
|
||||
<span>{selectedLocation}</span>
|
||||
<img
|
||||
className={openLocationList ? "rotate" : ""}
|
||||
src={arrowDown}
|
||||
/>
|
||||
</div>
|
||||
<p>
|
||||
При аутстаффе мы предоставляем вам IT-специалистов при этом они
|
||||
находятся в нашем штате.
|
||||
<br />
|
||||
<br />
|
||||
Вы сможете прособеседовать наших специалистов, посмотреть
|
||||
проекты и Git.
|
||||
</p>
|
||||
{openLocationList && (
|
||||
<div className="form__block__dropDown">
|
||||
{locationList.map((location, index) => {
|
||||
return (
|
||||
<p
|
||||
key={index}
|
||||
onClick={() => {
|
||||
setOpenLocationList(false);
|
||||
setSelectedLocation(location);
|
||||
}}
|
||||
>
|
||||
{location}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="partner-add-request__info__block">
|
||||
<div className="partner-add-request__info__block__title">
|
||||
<img src={reportImg} alt="reportImg" />
|
||||
<h4>Отчетность:</h4>
|
||||
<div className="form__block__section">
|
||||
<h3>Ставка</h3>
|
||||
<div className="form__block__section__input special__select">
|
||||
<input type="text" placeholder="Оплата" />
|
||||
</div>
|
||||
<p>
|
||||
Вы можете обратиться к специалисту напрямую.
|
||||
<br />
|
||||
<br />
|
||||
Каждый день специалисты описывают выполненные работы и
|
||||
затраченные на это часы.
|
||||
<br />
|
||||
<br />
|
||||
Можем выделить руководителя проекта и тестировщиков.
|
||||
</p>
|
||||
</div>
|
||||
<div className="partner-add-request__info__block">
|
||||
<div className="partner-add-request__info__block__title">
|
||||
<img src={documentsImg} alt="documentsImg" />
|
||||
<h4>
|
||||
Обмен <br />
|
||||
документами:
|
||||
</h4>
|
||||
</div>
|
||||
<div className="form__block__buttons">
|
||||
<Link to="/profile/requests" className="form__block__cancel">
|
||||
Отмена
|
||||
</Link>
|
||||
<button
|
||||
onClick={() => handler()}
|
||||
className={
|
||||
disableBtn()
|
||||
? "form__block__save"
|
||||
: "form__block__save disable"
|
||||
}
|
||||
>
|
||||
Сохранить
|
||||
</button>
|
||||
<p>
|
||||
В Личном кабинете платформы получайте отчеты выполненных работ и
|
||||
счеты на согласование и оплату
|
||||
Нажимая "Сохранить", вы соглашаетесь с Правилами обработки и
|
||||
использования персональных данных
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -23,6 +23,31 @@
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
&__main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 30px;
|
||||
}
|
||||
|
||||
&__special {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
border-radius: 12px;
|
||||
padding: 41px 45px 35px 55px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
h4 {
|
||||
font-weight: 700;
|
||||
color: rgba(91, 104, 113, 1);
|
||||
font-size: 20px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.special__select {
|
||||
max-width: 450px;
|
||||
}
|
||||
}
|
||||
|
||||
&__section {
|
||||
margin-top: 25px;
|
||||
display: flex;
|
||||
@ -99,6 +124,7 @@
|
||||
font-size: 15px;
|
||||
line-height: 18px;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,13 +190,23 @@
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
margin-top: 50px;
|
||||
|
||||
button {
|
||||
max-width: 150px;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 300;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
color: rgba(0, 0, 0, 1);
|
||||
margin-left: 50px;
|
||||
max-width: 360px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
&__cancel {
|
||||
@ -321,7 +357,7 @@
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
width: 100%;
|
||||
padding: 74px 48px 136px 62px;
|
||||
padding: 74px 48px 67px 62px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 61px;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import moment from "moment/moment";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Navigate, useParams } from "react-router-dom";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import { getRequestDates, setRequestDate } from "@redux/reportSlice";
|
||||
|
||||
|
@ -44,9 +44,25 @@ export const PartnerCategories = () => {
|
||||
const theme = useTheme(getTheme());
|
||||
const [nodes, setNodes] = useState([]);
|
||||
const [initialNodes, setInitialNodes] = useState([]);
|
||||
const [activeTab, setActiveTab] = useState(1);
|
||||
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Все"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Фронтенд"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Бэкенд"
|
||||
}
|
||||
];
|
||||
|
||||
const COLUMNS = [
|
||||
{
|
||||
label: "",
|
||||
@ -256,6 +272,21 @@ export const PartnerCategories = () => {
|
||||
<div className="partner-categories__items">
|
||||
{Boolean(initialNodes.length) ? (
|
||||
<>
|
||||
<div className="table__tabs">
|
||||
{tabs.map((tab) => {
|
||||
return (
|
||||
<button
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
key={tab.id}
|
||||
className={`table__tab ${
|
||||
tab.id === activeTab ? "table__tab--active" : ""
|
||||
}`}
|
||||
>
|
||||
{tab.name}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<label className="table__search" htmlFor="search">
|
||||
Поиск по имени:
|
||||
<input
|
||||
|
@ -204,6 +204,29 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
margin: 0 auto 36px 18px;
|
||||
background: rgba(240, 242, 245, 1);
|
||||
padding: 4px 8px 4px 8px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__tab {
|
||||
color: rgba(46, 58, 89, 1);
|
||||
font-size: 15px;
|
||||
outline: none;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0 12px;
|
||||
|
||||
&--active {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
border-radius: 5px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&__pagination {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
@ -11,7 +11,8 @@ import { Navigation } from "@components/Navigation/Navigation";
|
||||
import { ProfileBreadcrumbs } from "@components/ProfileBreadcrumbs/ProfileBreadcrumbs";
|
||||
import { ProfileHeader } from "@components/ProfileHeader/ProfileHeader";
|
||||
|
||||
// import paymentIcon from "assets/icons/paymentIcon.png";
|
||||
import financeIcon from "assets/icons/financeIcon.png";
|
||||
import paymentIcon from "assets/icons/paymentIcon.png";
|
||||
import settingIcon from "assets/icons/settingIcon.png";
|
||||
import summaryIcon from "assets/icons/summaryIcon.png";
|
||||
import timerIcon from "assets/icons/timerIcon.png";
|
||||
@ -28,10 +29,10 @@ export const Profile = () => {
|
||||
const [profileItemsInfo] = useState({
|
||||
developer: [
|
||||
{
|
||||
path: "profile/calendar",
|
||||
img: reportsIcon,
|
||||
title: "Ваша отчетность",
|
||||
description: "<span></span>Отработанных в этом месяце часов"
|
||||
path: "profile",
|
||||
img: paymentIcon,
|
||||
title: "Работа в IT <br/>открытые запросы",
|
||||
description: "Перейдите чтобы посмотреть <br/>открытые позиции"
|
||||
},
|
||||
{
|
||||
path: "profile/summary",
|
||||
@ -42,20 +43,26 @@ export const Profile = () => {
|
||||
{
|
||||
path: "profile/tracker",
|
||||
img: timerIcon,
|
||||
title: "Трекер времени",
|
||||
title: "Трекер <br/>времени",
|
||||
description: "Сколько времени занимает <br/>выполнение задач"
|
||||
},
|
||||
// {
|
||||
// path: "profile/payouts",
|
||||
// img: paymentIcon,
|
||||
// title: "Выплаты",
|
||||
// description: "У вас <span>подтвержден</span> <br/>статус самозанятого"
|
||||
// },
|
||||
{
|
||||
path: "profile/payouts",
|
||||
img: financeIcon,
|
||||
title: "Выплаты и <br/>финансы",
|
||||
description: "У вас <span>подтвержден</span> <br/>статус самозанятого"
|
||||
},
|
||||
{
|
||||
path: "profile/settings",
|
||||
img: settingIcon,
|
||||
title: "Настройки профиля",
|
||||
title: "Настройки <br/>профиля",
|
||||
description: "Перейдите чтобы начать <br/>редактирование"
|
||||
},
|
||||
{
|
||||
path: "profile/calendar",
|
||||
img: reportsIcon,
|
||||
title: "Ваша <br/>отчетность",
|
||||
description: "<span></span>Отработанных в этом месяце часов"
|
||||
}
|
||||
],
|
||||
partner: [
|
||||
|
@ -1,20 +1,15 @@
|
||||
import moment from "moment";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { HexColorPicker } from "react-colorful";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
|
||||
import {
|
||||
activeLoader,
|
||||
addNewTagToProject,
|
||||
deleteTagProject,
|
||||
filterCreatedByMe,
|
||||
filteredExecutorTasks,
|
||||
filteredParticipateTasks,
|
||||
getBoarderLoader,
|
||||
getProjectBoard,
|
||||
modalToggle,
|
||||
movePositionProjectTask,
|
||||
moveProjectTask,
|
||||
setColumnId,
|
||||
setColumnName,
|
||||
@ -23,7 +18,7 @@ import {
|
||||
setToggleTab
|
||||
} from "@redux/projectsTrackerSlice";
|
||||
|
||||
import { removeLast, urlForLocal } from "@utils/helper";
|
||||
import { urlForLocal } from "@utils/helper";
|
||||
|
||||
import { apiRequest } from "@api/request";
|
||||
|
||||
@ -39,25 +34,20 @@ import TrackerModal from "@components/Modal/Tracker/TrackerModal/TrackerModal";
|
||||
import { Navigation } from "@components/Navigation/Navigation";
|
||||
import { ProfileBreadcrumbs } from "@components/ProfileBreadcrumbs/ProfileBreadcrumbs";
|
||||
import { ProfileHeader } from "@components/ProfileHeader/ProfileHeader";
|
||||
import TrackerSelectColumn from "@components/TrackerSelectColumn/TrackerSelectColumn";
|
||||
import TrackerCardTask from "@components/TrackerCardTask/TrackerCardTask";
|
||||
import TrackerSelectExecutor from "@components/TrackerSelectExecutor/TrackerSelectExecutor";
|
||||
import TrackerTagList from "@components/TrackerTagList/TrackerTagList";
|
||||
|
||||
import arrow from "assets/icons/arrows/arrowRight.png";
|
||||
import arrowDown from "assets/icons/arrows/selectArrow.png";
|
||||
import category from "assets/icons/category.svg";
|
||||
import close from "assets/icons/close.png";
|
||||
import commentsBoard from "assets/icons/commentsBoard.svg";
|
||||
import del from "assets/icons/delete.svg";
|
||||
import edit from "assets/icons/edit.svg";
|
||||
import filesBoard from "assets/icons/filesBoard.svg";
|
||||
import trackerNoTasks from "assets/icons/trackerNoTasks.svg";
|
||||
import project from "assets/icons/trackerProject.svg";
|
||||
import tasks from "assets/icons/trackerTasks.svg";
|
||||
import accept from "assets/images/accept.png";
|
||||
import archive from "assets/images/archiveIcon.png";
|
||||
import avatarMok from "assets/images/avatarMok.png";
|
||||
|
||||
import { getCorrectDate } from "../../utils/calendarHelper";
|
||||
|
||||
export const ProjectTracker = () => {
|
||||
const dispatch = useDispatch();
|
||||
const projectId = useParams();
|
||||
@ -66,43 +56,22 @@ export const ProjectTracker = () => {
|
||||
const [selectedTab, setSelectedTab] = useState(0);
|
||||
const [priorityTask, setPriorityTask] = useState(0);
|
||||
const [wrapperHover, setWrapperHover] = useState({});
|
||||
const [taskHover, setTaskHover] = useState({});
|
||||
const [modalAdd, setModalAdd] = useState(false);
|
||||
const [modalActiveTicket, setModalActiveTicket] = useState(false);
|
||||
const [selectedTicket, setSelectedTicket] = useState({});
|
||||
const [personListOpen, setPersonListOpen] = useState(false);
|
||||
const [tags, setTags] = useState({
|
||||
open: false,
|
||||
add: false,
|
||||
edit: false
|
||||
});
|
||||
const [acceptModalOpen, setAcceptModalOpen] = useState(false);
|
||||
const [currentColumnDelete, setCurrentColumnDelete] = useState(null);
|
||||
const [color, setColor] = useState("#aabbcc");
|
||||
const [tagInfo, setTagInfo] = useState({ description: "", name: "" });
|
||||
const [checkBoxParticipateTasks, setCheckBoxParticipateTasks] =
|
||||
useState(false);
|
||||
const [filteredNoTasks, setFilteredNoTasks] = useState(false);
|
||||
const [checkBoxMyTasks, setCheckBoxMyTasks] = useState(false);
|
||||
const [selectedExecutor, setSelectedExecutor] = useState(null);
|
||||
const [selectExecutorOpen, setSelectedExecutorOpen] = useState(false);
|
||||
const startWrapperIndexTest = useRef({});
|
||||
const projectBoard = useSelector(getProjectBoard);
|
||||
const loader = useSelector(getBoarderLoader);
|
||||
const { showNotification } = useNotification();
|
||||
|
||||
const priority = {
|
||||
2: "Высокий",
|
||||
1: "Средний",
|
||||
0: "Низкий"
|
||||
};
|
||||
|
||||
const priorityClass = {
|
||||
2: "high",
|
||||
1: "middle",
|
||||
0: "low"
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(activeLoader());
|
||||
dispatch(setProjectBoardFetch(projectId.id));
|
||||
@ -110,8 +79,6 @@ export const ProjectTracker = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const tasksHover = {};
|
||||
const columnHover = {};
|
||||
let columnsTasksEmpty = true;
|
||||
if (Object.keys(projectBoard).length) {
|
||||
projectBoard.columns.forEach((column) => {
|
||||
@ -120,8 +87,6 @@ export const ProjectTracker = () => {
|
||||
...prevState,
|
||||
[column.id]: false
|
||||
}));
|
||||
columnHover[column.id] = false;
|
||||
column.tasks.forEach((task) => (tasksHover[task.id] = false));
|
||||
});
|
||||
}
|
||||
if (
|
||||
@ -132,48 +97,8 @@ export const ProjectTracker = () => {
|
||||
} else {
|
||||
setFilteredNoTasks(false);
|
||||
}
|
||||
setWrapperHover(columnHover);
|
||||
setTaskHover(tasksHover);
|
||||
}, [projectBoard]);
|
||||
|
||||
function dragStartHandler(e, task, columnId) {
|
||||
startWrapperIndexTest.current = { task: task, index: columnId };
|
||||
}
|
||||
|
||||
function dragOverTaskHandler(e, task) {
|
||||
e.preventDefault();
|
||||
if (startWrapperIndexTest.current.task.id === task.id) {
|
||||
return;
|
||||
}
|
||||
setTaskHover((prevState) => ({ [prevState]: false, [task.id]: true }));
|
||||
}
|
||||
|
||||
function dragLeaveTaskHandler() {
|
||||
setTaskHover((prevState) => ({ [prevState]: false }));
|
||||
}
|
||||
|
||||
function dragEndTaskHandler() {
|
||||
setTaskHover((prevState) => ({ [prevState]: false }));
|
||||
setWrapperHover((prevState) => ({
|
||||
[prevState]: false
|
||||
}));
|
||||
}
|
||||
|
||||
function dragDropTaskHandler(e, task, column) {
|
||||
e.preventDefault();
|
||||
if (task.id === startWrapperIndexTest.current.task.id) {
|
||||
return;
|
||||
}
|
||||
const finishTask = column.tasks.indexOf(task);
|
||||
dispatch(
|
||||
movePositionProjectTask({
|
||||
startTask: startWrapperIndexTest.current.task,
|
||||
finishTask: task,
|
||||
finishIndex: finishTask
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function dragOverHandler(e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
@ -288,11 +213,6 @@ export const ProjectTracker = () => {
|
||||
setCheckBoxMyTasks(!checkBoxMyTasks);
|
||||
}
|
||||
|
||||
function executorFilter(user) {
|
||||
dispatch(filteredExecutorTasks(user.user_id));
|
||||
setSelectedExecutor(user);
|
||||
}
|
||||
|
||||
function deleteSelectedExecutorFilter() {
|
||||
setSelectedExecutor(null);
|
||||
setCheckBoxParticipateTasks(false);
|
||||
@ -300,81 +220,6 @@ export const ProjectTracker = () => {
|
||||
dispatch(setProjectBoardFetch(projectId.id));
|
||||
}
|
||||
|
||||
function addNewTag() {
|
||||
apiRequest("/mark/create", {
|
||||
method: "POST",
|
||||
data: {
|
||||
title: tagInfo.description,
|
||||
slug: tagInfo.name,
|
||||
color: color,
|
||||
status: 1
|
||||
}
|
||||
}).then((data) => {
|
||||
apiRequest("/mark/attach", {
|
||||
method: "POST",
|
||||
data: {
|
||||
mark_id: data.id,
|
||||
entity_type: 1,
|
||||
entity_id: projectId.id
|
||||
}
|
||||
}).then((data) => {
|
||||
dispatch(addNewTagToProject(data.mark));
|
||||
setTags((prevState) => ({
|
||||
...prevState,
|
||||
add: false
|
||||
}));
|
||||
showNotification({
|
||||
show: true,
|
||||
text: "Тег успешно создан",
|
||||
type: "success"
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
}));
|
||||
setTagInfo({ description: "", name: "" });
|
||||
setColor("#aabbcc");
|
||||
showNotification({
|
||||
show: true,
|
||||
text: "Тег успешно изменён",
|
||||
type: "success"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function deleteTag(tagId) {
|
||||
apiRequest("/mark/detach", {
|
||||
method: "DELETE",
|
||||
data: {
|
||||
mark_id: tagId,
|
||||
entity_type: 1,
|
||||
entity_id: projectId.id
|
||||
}
|
||||
}).then(() => {
|
||||
dispatch(deleteTagProject(tagId));
|
||||
showNotification({
|
||||
show: true,
|
||||
text: "Тег удален",
|
||||
type: "success"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const initListeners = () => {
|
||||
document.addEventListener("click", closeByClickingOut);
|
||||
};
|
||||
@ -394,32 +239,6 @@ export const ProjectTracker = () => {
|
||||
setPersonListOpen(false);
|
||||
}
|
||||
|
||||
if (
|
||||
event &&
|
||||
!path.find(
|
||||
(div) =>
|
||||
div.classList &&
|
||||
(div.classList.contains("tasks__head__executor") ||
|
||||
div.classList.contains("tasks__head__executor-dropdown"))
|
||||
)
|
||||
) {
|
||||
setSelectedExecutorOpen(false);
|
||||
}
|
||||
|
||||
if (
|
||||
event &&
|
||||
!path.find(
|
||||
(div) =>
|
||||
div.classList &&
|
||||
(div.classList.contains("tasks__head__tags") ||
|
||||
div.classList.contains("tags__list"))
|
||||
)
|
||||
) {
|
||||
setTags({ open: false, add: false, edit: false });
|
||||
setTagInfo({ description: "", name: "" });
|
||||
setColor("#aabbcc");
|
||||
}
|
||||
|
||||
if (
|
||||
event &&
|
||||
!path.find(
|
||||
@ -577,207 +396,16 @@ export const ProjectTracker = () => {
|
||||
{checkBoxMyTasks && <img src={accept} alt="accept" />}
|
||||
</div>
|
||||
</div>
|
||||
{selectedExecutor ? (
|
||||
<div className="tasks__head__executor-selected">
|
||||
<p>{removeLast(selectedExecutor.user.fio)}</p>
|
||||
<img
|
||||
className="avatar"
|
||||
src={
|
||||
selectedExecutor.user?.avatar
|
||||
? urlForLocal(selectedExecutor.user.avatar)
|
||||
: avatarMok
|
||||
}
|
||||
alt="avatar"
|
||||
/>
|
||||
<img
|
||||
className="delete"
|
||||
src={close}
|
||||
alt="delete"
|
||||
onClick={deleteSelectedExecutorFilter}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className="tasks__head__executor"
|
||||
onClick={() =>
|
||||
setSelectedExecutorOpen(!selectExecutorOpen)
|
||||
}
|
||||
>
|
||||
<p>Выберите исполнителя</p>
|
||||
<img
|
||||
className={selectExecutorOpen ? "open" : ""}
|
||||
src={arrowDown}
|
||||
alt="arrow"
|
||||
/>
|
||||
{selectExecutorOpen && (
|
||||
<div className="tasks__head__executor-dropdown">
|
||||
{projectBoard.projectUsers.map((user) => {
|
||||
return (
|
||||
<div
|
||||
className="executor-dropdown__person"
|
||||
key={user.user_id}
|
||||
onClick={() => executorFilter(user)}
|
||||
>
|
||||
<p>{removeLast(user.user?.fio)}</p>
|
||||
<img
|
||||
src={
|
||||
user.user?.avatar
|
||||
? urlForLocal(user.user.avatar)
|
||||
: avatarMok
|
||||
}
|
||||
alt="avatar"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="tasks__head__tags">
|
||||
<div
|
||||
className="tags__add"
|
||||
onClick={() => {
|
||||
setTags((prevState) => ({
|
||||
...prevState,
|
||||
open: !tags.open
|
||||
}));
|
||||
}}
|
||||
>
|
||||
<p>Список тегов</p>
|
||||
<img
|
||||
className={tags.open ? "open" : ""}
|
||||
src={arrowDown}
|
||||
alt="arrow"
|
||||
/>
|
||||
</div>
|
||||
{tags.open && (
|
||||
<div className="tags__list">
|
||||
{!tags.add && !tags.edit && (
|
||||
<div
|
||||
className="add-new-tag"
|
||||
onClick={() =>
|
||||
setTags((prevState) => ({
|
||||
...prevState,
|
||||
add: true
|
||||
}))
|
||||
}
|
||||
>
|
||||
<p>Добавить новый тег</p>
|
||||
<span>+</span>
|
||||
</div>
|
||||
)}
|
||||
{!tags.add && !tags.edit && (
|
||||
<div className="tags__list__created">
|
||||
{projectBoard.mark.map((tag) => {
|
||||
return (
|
||||
<div className="tag-item" key={tag.id}>
|
||||
<div className="tag-item__info">
|
||||
<span
|
||||
className="tag-item__color"
|
||||
style={{ background: tag.color }}
|
||||
/>
|
||||
<div>
|
||||
<span className="tag-item__info__name">
|
||||
{tag.slug}
|
||||
</span>
|
||||
<p className="tag-item__description">
|
||||
{tag.title}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="tag-item__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>
|
||||
)}
|
||||
{(tags.add || tags.edit) && (
|
||||
<div className="form-tag">
|
||||
<input
|
||||
className="form-tag__input"
|
||||
placeholder="Описание метки"
|
||||
maxLength="25"
|
||||
value={tagInfo.description}
|
||||
onChange={(e) =>
|
||||
setTagInfo((prevState) => ({
|
||||
...prevState,
|
||||
description: e.target.value
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<input
|
||||
className="form-tag__input"
|
||||
placeholder="Тег"
|
||||
value={tagInfo.name}
|
||||
maxLength="10"
|
||||
onChange={(e) =>
|
||||
setTagInfo((prevState) => ({
|
||||
...prevState,
|
||||
name: e.target.value
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<HexColorPicker color={color} onChange={setColor} />
|
||||
<button
|
||||
onClick={() => {
|
||||
tags.add ? addNewTag() : editTag();
|
||||
}}
|
||||
className={
|
||||
tagInfo.name && tagInfo.description
|
||||
? "form-tag__btn"
|
||||
: "form-tag__btn disable"
|
||||
}
|
||||
>
|
||||
{tags.add ? "Добавить" : "Изменить"}
|
||||
</button>
|
||||
{(tags.add || tags.edit) && (
|
||||
<button
|
||||
className={"form-tag__btn"}
|
||||
onClick={() => {
|
||||
setTags((prevState) => ({
|
||||
...prevState,
|
||||
add: false,
|
||||
edit: false
|
||||
}));
|
||||
setTagInfo({
|
||||
description: "",
|
||||
name: ""
|
||||
});
|
||||
setColor("#aabbcc");
|
||||
}}
|
||||
>
|
||||
Отмена
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<TrackerSelectExecutor
|
||||
deleteSelectedExecutor={deleteSelectedExecutorFilter}
|
||||
projectBoard={projectBoard}
|
||||
selectedExecutor={selectedExecutor}
|
||||
setSelectedExecutor={setSelectedExecutor}
|
||||
/>
|
||||
|
||||
<TrackerTagList projectBoard={projectBoard} />
|
||||
|
||||
<Link to="/profile/tracker" className="tasks__head__back">
|
||||
<p>К списку проектов</p>
|
||||
<img src={arrow} alt="arrow" />
|
||||
@ -878,7 +506,7 @@ export const ProjectTracker = () => {
|
||||
</div>
|
||||
)}
|
||||
<div className="tasks-container">
|
||||
{column.tasks.map((task) => {
|
||||
{column.tasks.map((task, index) => {
|
||||
const dateDeadline = new Date(task.dead_line);
|
||||
const currentDate = moment().format(
|
||||
"YYYY-MM-DD HH:mm:ss"
|
||||
@ -889,119 +517,16 @@ export const ProjectTracker = () => {
|
||||
? "red"
|
||||
: "#1a1919";
|
||||
return (
|
||||
<div
|
||||
key={task.id}
|
||||
className={`tasks__board__item ${
|
||||
taskHover[task.id] ? "task__hover" : ""
|
||||
}`}
|
||||
draggable={true}
|
||||
onDragStart={(e) =>
|
||||
dragStartHandler(e, task, column.id)
|
||||
}
|
||||
onDragOver={(e) => dragOverTaskHandler(e, task)}
|
||||
onDragLeave={(e) => dragLeaveTaskHandler(e)}
|
||||
onDragEnd={() => dragEndTaskHandler()}
|
||||
onDrop={(e) =>
|
||||
dragDropTaskHandler(e, task, column)
|
||||
}
|
||||
onClick={(e) => openTicket(e, task)}
|
||||
>
|
||||
<div
|
||||
className="tasks__board__item__title"
|
||||
onClick={() => {
|
||||
if (window.innerWidth < 985) {
|
||||
window.location.replace(
|
||||
`/tracker/task/${task.id}`
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<p className="task__board__item__title">
|
||||
{task.title}
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: task.description
|
||||
}}
|
||||
className="tasks__board__item__description"
|
||||
></p>
|
||||
{Boolean(task.mark.length) && (
|
||||
<div className="tasks__board__item__tags">
|
||||
{task.mark.map((tag) => {
|
||||
return (
|
||||
<div
|
||||
className="tag-item"
|
||||
key={tag.id}
|
||||
style={{ background: tag.color }}
|
||||
>
|
||||
<p>{tag.slug}</p>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<div className="tasks__board__item__container">
|
||||
{typeof task.execution_priority ===
|
||||
"number" && (
|
||||
<div className="tasks__board__item__priority">
|
||||
<p>⚡</p>
|
||||
<span
|
||||
className={
|
||||
priorityClass[task.execution_priority]
|
||||
}
|
||||
>
|
||||
{priority[task.execution_priority]}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{task.dead_line && (
|
||||
<div className="tasks__board__item__dead-line">
|
||||
<p>⌛</p>
|
||||
<span style={{ color: titleColor }}>
|
||||
{getCorrectDate(task.dead_line)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="tasks__board__item__info">
|
||||
<div className="tasks__board__item__executor">
|
||||
<img
|
||||
src={
|
||||
task.executor?.avatar
|
||||
? urlForLocal(task.executor?.avatar)
|
||||
: avatarMok
|
||||
}
|
||||
alt="avatar"
|
||||
/>
|
||||
<span>
|
||||
{removeLast(task.executor?.fio) ||
|
||||
"Исполнитель не назначен"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="tasks__board__item__info__tags">
|
||||
<div className="tasks__board__item__info__more">
|
||||
<img
|
||||
src={commentsBoard}
|
||||
alt="commentsImg"
|
||||
/>
|
||||
<span>{task.comment_count}</span>
|
||||
</div>
|
||||
<div className="tasks__board__item__info__more">
|
||||
<img src={filesBoard} alt="filesImg" />
|
||||
<span>{task.file_count}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<TrackerSelectColumn
|
||||
columns={projectBoard.columns.filter(
|
||||
(item) => item.id !== column.id
|
||||
)}
|
||||
currentColumn={column}
|
||||
task={task}
|
||||
/>
|
||||
</div>
|
||||
<TrackerCardTask
|
||||
column={column}
|
||||
key={index}
|
||||
openTicket={openTicket}
|
||||
projectBoard={projectBoard}
|
||||
setWrapperHover={setWrapperHover}
|
||||
startWrapperIndexTest={startWrapperIndexTest}
|
||||
task={task}
|
||||
titleColor={titleColor}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
@ -3,13 +3,102 @@ import SVG from "react-inlinesvg";
|
||||
|
||||
import { AuthHeader } from "@components/Common/AuthHeader/AuthHeader";
|
||||
|
||||
import arrowReviewsLeft from "assets/icons/arrows/arrowReviewsLeft.png";
|
||||
import arrowReviewsRight from "assets/icons/arrows/arrowReviewsRight.png";
|
||||
import Ellipse from "assets/images/EllipseIntro.svg";
|
||||
import backgroundOpp from "assets/images/backgroundOpportunity.png";
|
||||
import cat from "assets/images/cat.png";
|
||||
import clue from "assets/images/clue.png";
|
||||
import code1 from "assets/images/landingBackgroundCode1.png";
|
||||
import code from "assets/images/landingBackgroundCode.png";
|
||||
import reviewsImgMok from "assets/images/reviewsImgMok.png";
|
||||
import flag from "assets/images/stackProjectsFlag.png";
|
||||
import fly from "assets/images/stackProjectsFly.png";
|
||||
import hand from "assets/images/stackProjectsHand.png";
|
||||
import rabota from "assets/images/stackProjectsRabota.png";
|
||||
import portfolio from "assets/images/stackSteptsPortfolio.png";
|
||||
|
||||
import "./stack.scss";
|
||||
|
||||
export const Stack = () => {
|
||||
const subjects = [
|
||||
{
|
||||
name: "Backend",
|
||||
skills: [
|
||||
"php",
|
||||
"yii2",
|
||||
"laravel",
|
||||
"symfony",
|
||||
"django",
|
||||
"nodejs",
|
||||
"fastAPI",
|
||||
"flask",
|
||||
"python",
|
||||
"exspress",
|
||||
"adonis"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Front",
|
||||
skills: [
|
||||
"react",
|
||||
"next.js",
|
||||
"typescript",
|
||||
"redux",
|
||||
"angular",
|
||||
"vue",
|
||||
"jquery",
|
||||
"css (sass/scss, tailwind, bootstrap, БЭМ)"
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const projects = [
|
||||
{
|
||||
description:
|
||||
"Импортозамещение в управлении проектами <span>таск-трекер ITGuild</span>",
|
||||
img: flag,
|
||||
name: "flag"
|
||||
},
|
||||
{
|
||||
description:
|
||||
"<span>Работа Тудей</span> - это сервис, который специализируется на поиске работы на новых территориях Российской Федерации.",
|
||||
img: rabota,
|
||||
name: "rabota"
|
||||
},
|
||||
{
|
||||
description:
|
||||
"<span>Внедрение искусственного интеллекта</span> (ИИ) в IT-проекты. Интеграции любых популярных сервисов.",
|
||||
img: hand,
|
||||
name: hand
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Новостной портал и удобный каталог компаний <span>DaInfo.pro</span> предоставляющих различные услуги и товары.",
|
||||
img: fly,
|
||||
name: "fly"
|
||||
}
|
||||
];
|
||||
|
||||
const steps = [
|
||||
{
|
||||
miniInfo: "Окунитесь в экосистему ITGUIL",
|
||||
info: "<span>уточнение</span> деталей и <span>обсуждение</span> условий с менеджером ITGUILD"
|
||||
},
|
||||
{
|
||||
miniInfo: "Окунитесь в экосистему ITGUIL",
|
||||
info: "<span>подписание договора</span> без обязательств оплаты на данном этапе"
|
||||
},
|
||||
{
|
||||
miniInfo: "Окунитесь в экосистему ITGUIL",
|
||||
info: "<span>формирование</span> команды или подбор отдельных специалистов под требования клиентов"
|
||||
},
|
||||
{
|
||||
miniInfo: "Окунитесь в экосистему ITGUIL",
|
||||
info: "<span>интеграция специалистов</span> в команду клиента, ежедневная отчетность под контролем менеджера ITGUILD"
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="stack">
|
||||
<AuthHeader />
|
||||
@ -54,7 +143,200 @@ export const Stack = () => {
|
||||
</div>
|
||||
</section>
|
||||
<section className="stack__opportunity">
|
||||
<div className="stack__container"></div>
|
||||
<img src={backgroundOpp} className="background__opportunity--left" />
|
||||
<img src={backgroundOpp} className="background__opportunity--right" />
|
||||
<div className="stack__container opportunity__container">
|
||||
<img src={code} className="opportunity__code" />
|
||||
<img src={code} className="opportunity__code--center" />
|
||||
<div className="opportunity__block">
|
||||
<h3 className="opportunity__title">Stack</h3>
|
||||
<div className="opportunity__info">
|
||||
<span className="info__subtitle">
|
||||
Окунитесь в экосистему ITGUIL
|
||||
</span>
|
||||
<p className="info__about">
|
||||
<span>Вы получаете полное управление над сотрудниками,</span>{" "}
|
||||
имея возможность контролировать и заменять IT штат.
|
||||
</p>
|
||||
<div className="info__notification">
|
||||
<img src={clue} alt="clue" />
|
||||
<p>
|
||||
Можем подготовить специалиста конкретно под ваш проект и
|
||||
используемый стек. Таким образом вы сможете сэкономить ресурсы
|
||||
на поиск кандидата.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="opportunity__subjects">
|
||||
{subjects.map((subject) => {
|
||||
return (
|
||||
<div className="subject" key={subject.name}>
|
||||
<h4>{subject.name}</h4>
|
||||
<div className="subject__skills">
|
||||
{subject.skills.map((skill) => {
|
||||
return <span key={skill}>{skill}</span>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="stack__projects projects">
|
||||
<div className="stack__container projects__container">
|
||||
<img className="projects__code" src={code} alt="code" />
|
||||
<h3 className="projects__title">ITGUILD</h3>
|
||||
<div className="projects__block">
|
||||
<h4>Наши проекты</h4>
|
||||
<div className="projects__examples">
|
||||
{projects.map((project, index) => {
|
||||
return (
|
||||
<div key={index} className="stack__project">
|
||||
<span className="project__img">
|
||||
<img
|
||||
className={project.name}
|
||||
src={project.img}
|
||||
alt="img"
|
||||
/>
|
||||
</span>
|
||||
<p
|
||||
dangerouslySetInnerHTML={{ __html: project.description }}
|
||||
></p>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="projects__info">
|
||||
<p>
|
||||
<span>Мы обеспечиваем</span> финансовые, юридические и кадровые
|
||||
гарантии, предоставляем SLA и берем на себя ответственность за
|
||||
работу команды. Вам не требуется заниматься поиском, оформлением
|
||||
или увольнением сотрудников —{" "}
|
||||
<span>мы берем на себя все хлопоты.</span>
|
||||
</p>
|
||||
<button>оставить заявку</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="stack__steps">
|
||||
<div className="stack__container steps__container">
|
||||
<div className="steps__head">
|
||||
<h4>как это работает?</h4>
|
||||
<div className="steps__info">
|
||||
<p>
|
||||
Аутстаффинг представляет собой специфическую модель найма
|
||||
персонала, отличающуюся от аутсорсинга.
|
||||
</p>
|
||||
<p>
|
||||
<span>
|
||||
В контексте аутстаффинга вы нанимаете специалистов в области
|
||||
ИТ,
|
||||
</span>{" "}
|
||||
оплачивая их по их конкретным навыкам, и берете на себя
|
||||
организацию их работы.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="steps__items">
|
||||
{steps.map((step, index) => {
|
||||
return (
|
||||
<div key={index} className="item__wrapper">
|
||||
<div className="item__head">
|
||||
<h4>{`${index + 1}.`}</h4>
|
||||
<p>{step.miniInfo}</p>
|
||||
</div>
|
||||
<div className="steps__item" key={index}>
|
||||
<p
|
||||
className="item__info"
|
||||
dangerouslySetInnerHTML={{ __html: step.info }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="steps__portfolio">
|
||||
<img src={portfolio} alt="portfolio" />
|
||||
</div>
|
||||
<img
|
||||
className="steps__code steps__code--first"
|
||||
src={code}
|
||||
alt="code"
|
||||
/>
|
||||
<img
|
||||
className="steps__code steps__code--second"
|
||||
src={code}
|
||||
alt="code"
|
||||
/>
|
||||
</div>
|
||||
<img src={backgroundOpp} className="steps__background" />
|
||||
</section>
|
||||
<section className="stack__reviews">
|
||||
<div className="stack__container reviews__container">
|
||||
<div className="reviews__info">
|
||||
<div className="reviews__info-counter">375</div>
|
||||
<span>Довольных клиентов</span>
|
||||
<p>
|
||||
Предоставляем на аутстаффинг frontend- и backend - разработчиков
|
||||
уровня от junior до middle+
|
||||
</p>
|
||||
<p>
|
||||
Можем сделать оценку проекта, ревью кода, составить коммерческое
|
||||
предложение, рекомендации касаемо стека технологий и организации
|
||||
архитектуры разрабатываемого проекта.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="reviews__content">
|
||||
<h4>Что о нас говорят</h4>
|
||||
<div className="reviews__content-container">
|
||||
<div className="review">
|
||||
<div className="review__client">
|
||||
<img src={reviewsImgMok} alt="reviewsImgMok" />
|
||||
<span>Александр Гузеев</span>
|
||||
<p>Руководитель проекта ООО “ЭЛАР”</p>
|
||||
</div>
|
||||
|
||||
<div className="review__comment">
|
||||
<p>
|
||||
Команда ITGUILD берется за решение широкого круга задач, не
|
||||
боясь при этом ни сжатых сроков, ни сложной специфики
|
||||
проектов, и успешно доводит их ло решения. <br />
|
||||
<br />
|
||||
Разаработчики Кирилла смогли не только усилить существующую
|
||||
команду разработки, став ее полноценной частью, но и
|
||||
привести в проект новые идеи, свои знания и опыт.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="reviews__content-buttons">
|
||||
<button>
|
||||
<img src={arrowReviewsLeft} alt="" />
|
||||
</button>
|
||||
<button>
|
||||
<img src={arrowReviewsRight} alt="" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<img
|
||||
className="reviews__code reviews__code--first"
|
||||
src={code1}
|
||||
alt="code"
|
||||
/>
|
||||
<img
|
||||
className="reviews__code reviews__code--second"
|
||||
src={code1}
|
||||
alt="code"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section className="stack__contact">
|
||||
<div className="stack__container contact__container"></div>
|
||||
</section>
|
||||
</section>
|
||||
);
|
||||
|
@ -9,7 +9,7 @@
|
||||
display: flex;
|
||||
}
|
||||
&__intro {
|
||||
background: #EEEEEE;
|
||||
background: #eeeeee;
|
||||
|
||||
.intro {
|
||||
&__container {
|
||||
@ -32,23 +32,24 @@
|
||||
|
||||
&__title {
|
||||
font-weight: 900;
|
||||
color: #A7CA60;
|
||||
color: #a7ca60;
|
||||
font-size: 88px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
margin: 39px 0 6px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
letter-spacing: 0.05em;
|
||||
font-size: 39px;
|
||||
font-weight: 700;
|
||||
color: #4A4A4A;
|
||||
color: #4a4a4a;
|
||||
}
|
||||
|
||||
&__about {
|
||||
max-width: 380px;
|
||||
color: #4A4A4A;
|
||||
color: #4a4a4a;
|
||||
font-size: 14px;
|
||||
font-weight: 250;
|
||||
margin-bottom: 34px;
|
||||
@ -67,14 +68,14 @@
|
||||
&__link {
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
color: #A7CA60;
|
||||
color: #a7ca60;
|
||||
}
|
||||
|
||||
&__ellipse {
|
||||
z-index: 1;
|
||||
top: 65%;
|
||||
left:50%;
|
||||
transform:translate(-50%, -50%);
|
||||
top: 45%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
@ -95,7 +96,11 @@
|
||||
position: absolute;
|
||||
backdrop-filter: blur(8.699999809265137px);
|
||||
box-shadow: 10px 9px 14px 0 rgba(0, 0, 0, 0.06);
|
||||
background: linear-gradient(137deg, rgba(255, 255, 255, 0.34) 0%, rgba(206, 198, 198, 0.34) 100%);
|
||||
background: linear-gradient(
|
||||
137deg,
|
||||
rgba(255, 255, 255, 0.34) 0%,
|
||||
rgba(206, 198, 198, 0.34) 100%
|
||||
);
|
||||
border-radius: 8px;
|
||||
top: -35px;
|
||||
left: -25px;
|
||||
@ -105,7 +110,7 @@
|
||||
.aside {
|
||||
&__logo {
|
||||
z-index: 2;
|
||||
font-family: 'Geraspoheko';
|
||||
font-family: "Geraspoheko";
|
||||
color: white;
|
||||
font-size: 343px;
|
||||
position: absolute;
|
||||
@ -118,7 +123,11 @@
|
||||
position: absolute;
|
||||
backdrop-filter: blur(8.699999809265137px);
|
||||
box-shadow: 10px 9px 14px 0 rgba(0, 0, 0, 0.03);
|
||||
background: linear-gradient(137deg, rgba(255, 255, 255, 0.34) 0%, rgba(206, 198, 198, 0.34) 100%);
|
||||
background: linear-gradient(
|
||||
137deg,
|
||||
rgba(255, 255, 255, 0.34) 0%,
|
||||
rgba(206, 198, 198, 0.34) 100%
|
||||
);
|
||||
border-radius: 8px;
|
||||
width: 182px;
|
||||
height: 106px;
|
||||
@ -128,7 +137,11 @@
|
||||
display: flex;
|
||||
padding: 24px 20px 18px 30px;
|
||||
border: 0.5px solid;
|
||||
border-image-source: linear-gradient(137.79deg, #FFFFFF 9.15%, #F4F4F4 76.22%);
|
||||
border-image-source: linear-gradient(
|
||||
137.79deg,
|
||||
#ffffff 9.15%,
|
||||
#f4f4f4 76.22%
|
||||
);
|
||||
|
||||
p {
|
||||
color: rgba(141, 141, 141, 1);
|
||||
@ -145,7 +158,7 @@
|
||||
img {
|
||||
position: absolute;
|
||||
top: -25px;
|
||||
left: 0 ;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&:before {
|
||||
@ -175,15 +188,643 @@
|
||||
&__button {
|
||||
max-width: 200px;
|
||||
width: 100%;
|
||||
background: #A7CA60;
|
||||
background: #a7ca60;
|
||||
font-size: 15px;
|
||||
color: #4A4A4A;
|
||||
color: #4a4a4a;
|
||||
padding: 14px 0;
|
||||
border-radius: 44px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
&__opportunity {
|
||||
background: #1E1E1E;
|
||||
background: #1e1e1e;
|
||||
position: relative;
|
||||
|
||||
.background__opportunity--left {
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -5%;
|
||||
}
|
||||
|
||||
.background__opportunity--right {
|
||||
position: absolute;
|
||||
bottom: -361px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.opportunity {
|
||||
&__container {
|
||||
padding: 105px 0 0px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__code {
|
||||
position: absolute;
|
||||
top: 35px;
|
||||
left: 55px;
|
||||
|
||||
&--center {
|
||||
position: absolute;
|
||||
right: 31%;
|
||||
top: 34%;
|
||||
}
|
||||
}
|
||||
|
||||
&__block {
|
||||
display: flex;
|
||||
}
|
||||
&__title {
|
||||
font-family: "Geraspoheko";
|
||||
font-weight: 400;
|
||||
font-size: 343px;
|
||||
line-height: 1.03;
|
||||
margin-bottom: 0;
|
||||
z-index: 2;
|
||||
background: linear-gradient(360deg, #171717 0%, #2a2a2a 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 15px;
|
||||
.info {
|
||||
&__subtitle {
|
||||
padding-left: 31px;
|
||||
color: rgba(167, 202, 96, 1);
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
line-height: 16.24px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
&__about {
|
||||
padding-left: 31px;
|
||||
font-size: 14px;
|
||||
color: rgba(238, 238, 238, 1);
|
||||
line-height: 19.18px;
|
||||
font-weight: 250;
|
||||
max-width: 355px;
|
||||
margin-bottom: 27px;
|
||||
|
||||
span {
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
&__notification {
|
||||
padding: 21px 19px 23px 31px;
|
||||
border-radius: 8px;
|
||||
backdrop-filter: blur(8.699999809265137px);
|
||||
box-shadow: 10px 9px 14px 0 rgba(0, 0, 0, 0.06);
|
||||
background: linear-gradient(
|
||||
137deg,
|
||||
rgba(87, 87, 87, 0.34) 0%,
|
||||
rgba(104, 104, 104, 0.34) 100%
|
||||
);
|
||||
position: relative;
|
||||
border: 0.5px solid #717171;
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
width: 80.93px;
|
||||
height: 74.19px;
|
||||
left: -68px;
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: rgba(238, 238, 238, 1);
|
||||
line-height: 19.18px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__subjects {
|
||||
display: flex;
|
||||
column-gap: 100px;
|
||||
position: relative;
|
||||
top: -100px;
|
||||
z-index: 3;
|
||||
right: -35px;
|
||||
|
||||
.subject {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
h4 {
|
||||
text-transform: uppercase;
|
||||
color: rgba(167, 202, 96, 1);
|
||||
letter-spacing: 0.03em;
|
||||
font-weight: 900;
|
||||
font-size: 88px;
|
||||
line-height: 86.58px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&__skills {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 14px;
|
||||
margin-top: 20px;
|
||||
|
||||
span {
|
||||
border: 0.5px solid rgba(167, 202, 96, 0.5);
|
||||
border-radius: 56px;
|
||||
padding: 8px 25px 8px;
|
||||
color: rgba(167, 202, 96, 1);
|
||||
font-size: 17px;
|
||||
line-height: 20.88px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__projects {
|
||||
background: rgba(238, 238, 238, 1);
|
||||
|
||||
.projects {
|
||||
&__container {
|
||||
padding-top: 190px;
|
||||
padding-bottom: 81px;
|
||||
}
|
||||
|
||||
&__code {
|
||||
position: absolute;
|
||||
left: -170px;
|
||||
top: 24px;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-weight: 400;
|
||||
font-size: 343px;
|
||||
background: linear-gradient(to bottom, #ffffff, #dbdbdb);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-family: "Geraspoheko";
|
||||
margin: 0 auto;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
top: -50px;
|
||||
z-index: 2;
|
||||
filter: drop-shadow(0px 0px 30px #00000021);
|
||||
}
|
||||
|
||||
&__block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
z-index: 3;
|
||||
h4 {
|
||||
font-weight: 900;
|
||||
font-size: 46px;
|
||||
line-height: 98%;
|
||||
letter-spacing: 0.03em;
|
||||
text-transform: uppercase;
|
||||
color: #4a4a4a;
|
||||
}
|
||||
}
|
||||
|
||||
&__examples {
|
||||
display: flex;
|
||||
margin-top: 98px;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
|
||||
.stack__project {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
row-gap: 35px;
|
||||
|
||||
.project__img {
|
||||
border-radius: 8px;
|
||||
width: 99px;
|
||||
height: 81px;
|
||||
background: #a7ca60;
|
||||
position: relative;
|
||||
|
||||
img {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.flag {
|
||||
bottom: 21px;
|
||||
right: -10px;
|
||||
}
|
||||
|
||||
.rabota {
|
||||
top: -40px;
|
||||
right: 25px;
|
||||
}
|
||||
|
||||
.hand {
|
||||
top: -45px;
|
||||
left: -44px;
|
||||
}
|
||||
|
||||
.fly {
|
||||
top: -30px;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 250;
|
||||
font-size: 14px;
|
||||
line-height: 129%;
|
||||
text-align: center;
|
||||
color: #4a4a4a;
|
||||
max-width: 226px;
|
||||
|
||||
span {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
margin: 56px auto 0;
|
||||
column-gap: 50px;
|
||||
align-items: center;
|
||||
border: 1px solid #f8f8f8;
|
||||
border-radius: 8px;
|
||||
padding: 47px 91px 47px 55px;
|
||||
|
||||
p {
|
||||
max-width: 620px;
|
||||
font-weight: 250;
|
||||
font-size: 14px;
|
||||
line-height: 129%;
|
||||
color: #4a4a4a;
|
||||
|
||||
span {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 15px 43px;
|
||||
font-weight: 700;
|
||||
font-size: 15px;
|
||||
color: #4a4a4a;
|
||||
background: #a7ca60;
|
||||
border-radius: 44px;
|
||||
border: none;
|
||||
max-height: 46px;
|
||||
width: 201px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__steps {
|
||||
background: rgb(30, 30, 30);
|
||||
padding: 90px 0 40px;
|
||||
position: relative;
|
||||
|
||||
.steps {
|
||||
&__container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
|
||||
h4 {
|
||||
font-weight: 900;
|
||||
font-size: 66px;
|
||||
line-height: 98%;
|
||||
letter-spacing: 0.03em;
|
||||
text-transform: uppercase;
|
||||
color: #a7ca60;
|
||||
max-width: 380px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 20px;
|
||||
max-width: 499px;
|
||||
|
||||
p {
|
||||
font-weight: 300;
|
||||
font-size: 15px;
|
||||
line-height: 140%;
|
||||
color: #bdbdbd;
|
||||
}
|
||||
|
||||
span {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
&__items {
|
||||
margin-top: 115px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.item {
|
||||
&__wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__head {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
color: #a7ca60;
|
||||
top: -45px;
|
||||
left: 20px;
|
||||
|
||||
h4 {
|
||||
margin-bottom: 0;
|
||||
font-weight: 700;
|
||||
font-size: 100px;
|
||||
text-transform: uppercase;
|
||||
line-height: 0.8;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.01em;
|
||||
color: #a7ca60;
|
||||
max-width: 114px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__item {
|
||||
position: relative;
|
||||
width: 235px;
|
||||
height: 153px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
backdrop-filter: blur(3px);
|
||||
box-shadow: 10px 9px 14px 0 rgba(0, 0, 0, 0.06);
|
||||
background: linear-gradient(
|
||||
137deg,
|
||||
rgba(87, 87, 87, 0.34) 0%,
|
||||
rgba(104, 104, 104, 0.34) 100%
|
||||
);
|
||||
border: 0.5px solid #717171;
|
||||
border-radius: 8px;
|
||||
|
||||
.item {
|
||||
&__info {
|
||||
font-size: 15px;
|
||||
line-height: 131%;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-weight: 250;
|
||||
max-width: 160px;
|
||||
|
||||
span {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__portfolio {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
display: flex;
|
||||
|
||||
img {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__code {
|
||||
position: absolute;
|
||||
|
||||
&--first {
|
||||
top: -40px;
|
||||
left: 70px;
|
||||
}
|
||||
|
||||
&--second {
|
||||
bottom: -40px;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.steps__background {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -260px;
|
||||
}
|
||||
}
|
||||
|
||||
&__reviews {
|
||||
background-color: #eeeeee;
|
||||
|
||||
.reviews {
|
||||
&__container {
|
||||
align-items: center;
|
||||
column-gap: 58px;
|
||||
padding-bottom: 48px;
|
||||
}
|
||||
|
||||
&__code {
|
||||
position: absolute;
|
||||
mix-blend-mode: plus-lighter;
|
||||
|
||||
&--first {
|
||||
top: 90px;
|
||||
left: -180px;
|
||||
}
|
||||
|
||||
&--second {
|
||||
bottom: 0;
|
||||
left: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
background-color: #a7ca60;
|
||||
border-radius: 24px 0 113px 0;
|
||||
padding: 50px 22px 64px 44px;
|
||||
max-width: 311px;
|
||||
z-index: 1;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
width: 182px;
|
||||
height: 106px;
|
||||
position: absolute;
|
||||
backdrop-filter: blur(8.699999809265137px);
|
||||
box-shadow: 10px 9px 14px 0 rgba(0, 0, 0, 0.06);
|
||||
background: linear-gradient(
|
||||
137deg,
|
||||
rgba(255, 255, 255, 0.34) 0%,
|
||||
rgba(206, 198, 198, 0.34) 100%
|
||||
);
|
||||
border-radius: 8px;
|
||||
top: 62px;
|
||||
left: 200px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
width: 182px;
|
||||
height: 106px;
|
||||
position: absolute;
|
||||
backdrop-filter: blur(8.699999809265137px);
|
||||
box-shadow: 10px 9px 14px 0 rgba(0, 0, 0, 0.06);
|
||||
background: linear-gradient(
|
||||
137deg,
|
||||
rgba(255, 255, 255, 0.34) 0%,
|
||||
rgba(206, 198, 198, 0.34) 100%
|
||||
);
|
||||
border-radius: 8px;
|
||||
bottom: -30px;
|
||||
left: -100px;
|
||||
}
|
||||
|
||||
&-counter {
|
||||
color: #ffffff;
|
||||
font-weight: 900;
|
||||
font-size: 124px;
|
||||
line-height: 122px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #ffffff;
|
||||
font-weight: 900;
|
||||
font-size: 29px;
|
||||
line-height: 31.61px;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
p {
|
||||
color: #607536;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
line-height: 17.22px;
|
||||
|
||||
&:last-child {
|
||||
color: #ffffff;
|
||||
font-weight: 300;
|
||||
font-size: 14px;
|
||||
margin-top: 37px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
margin: 0 0 45px 0;
|
||||
|
||||
h4 {
|
||||
text-transform: uppercase;
|
||||
color: #4a4a4a;
|
||||
font-weight: 900;
|
||||
font-size: 46px;
|
||||
line-height: 45.26px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
&-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
column-gap: 20px;
|
||||
|
||||
.review {
|
||||
display: grid;
|
||||
grid-template-columns: 45% 55%;
|
||||
align-items: center;
|
||||
max-width: 517px;
|
||||
padding: 35px;
|
||||
border-radius: 8px;
|
||||
border: 0.5px solid #ffffff;
|
||||
background: linear-gradient(137deg, #ffffff -10%, #dddddd 100%);
|
||||
box-shadow: inset;
|
||||
|
||||
&__client {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
color: #7e7e7e;
|
||||
font-weight: 300;
|
||||
font-size: 14px;
|
||||
line-height: 16.24px;
|
||||
margin-right: 48px;
|
||||
|
||||
img {
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #1e1e1e;
|
||||
font-weight: 500;
|
||||
font-size: 17px;
|
||||
line-height: 19.72px;
|
||||
margin: 18px 0 16px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__comment {
|
||||
color: #4a4a4a;
|
||||
font-weight: 250;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
background-color: #ffffff;
|
||||
width: 70px;
|
||||
height: 64px;
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
button:first-child {
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__contact {
|
||||
background-color: #1e1e1e;
|
||||
|
||||
.contact {
|
||||
&__container {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,9 +33,9 @@ export const Summary = () => {
|
||||
const profileInfo = useSelector(getProfileInfo);
|
||||
const [openGit, setOpenGit] = useState(false);
|
||||
const [gitInfo, setGitInfo] = useState([]);
|
||||
const [editSummeryOpen, setEditSummeryOpen] = useState(false);
|
||||
const [editSummaryOpen, setEditSummaryOpen] = useState(false);
|
||||
const [editSkills, setEditSkills] = useState(false);
|
||||
const [summery, setSummery] = useState("");
|
||||
const [summary, setSummary] = useState("");
|
||||
const [selectedSkills, setSelectedSkills] = useState([]);
|
||||
const [selectSkillsOpen, setSelectSkillsOpen] = useState(false);
|
||||
const [skillsList, seSkillsList] = useState([]);
|
||||
@ -48,7 +48,7 @@ export const Summary = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setSummery(profileInfo.vc_text);
|
||||
setSummary(profileInfo.vc_text);
|
||||
setSelectedSkills(profileInfo.skillValues);
|
||||
}, [profileInfo]);
|
||||
|
||||
@ -69,11 +69,11 @@ export const Summary = () => {
|
||||
}).then(() => {});
|
||||
}
|
||||
|
||||
function editSummery() {
|
||||
function editSummary() {
|
||||
apiRequest("/resume/edit-text", {
|
||||
method: "PUT",
|
||||
data: {
|
||||
resume: summery
|
||||
resume: summary
|
||||
}
|
||||
}).then(() => {
|
||||
showNotification({
|
||||
@ -221,21 +221,21 @@ export const Summary = () => {
|
||||
<div className="summary__sections__head">
|
||||
<h3>Опыт работы</h3>
|
||||
<button
|
||||
className={editSummeryOpen ? "edit" : ""}
|
||||
className={editSummaryOpen ? "edit" : ""}
|
||||
onClick={() => {
|
||||
if (editSummeryOpen) {
|
||||
editSummery();
|
||||
if (editSummaryOpen) {
|
||||
editSummary();
|
||||
}
|
||||
setEditSummeryOpen(!editSummeryOpen);
|
||||
setEditSummaryOpen(!editSummaryOpen);
|
||||
}}
|
||||
>
|
||||
{editSummeryOpen ? "Сохранить" : "Редактировать"}
|
||||
{editSummaryOpen ? "Сохранить" : "Редактировать"}
|
||||
</button>
|
||||
</div>
|
||||
{editSummeryOpen ? (
|
||||
{editSummaryOpen ? (
|
||||
<CKEditor
|
||||
editor={ClassicEditor}
|
||||
data={summery}
|
||||
data={summary}
|
||||
config={{
|
||||
removePlugins: [
|
||||
"CKFinderUploadAdapter",
|
||||
@ -243,22 +243,19 @@ export const Summary = () => {
|
||||
"EasyImage",
|
||||
"Image",
|
||||
"ImageCaption",
|
||||
"ImageStyle",
|
||||
"ImageToolbar",
|
||||
"ImageUpload",
|
||||
"MediaEmbed",
|
||||
"BlockQuote"
|
||||
"MediaEmbed"
|
||||
]
|
||||
}}
|
||||
onChange={(event, editor) => {
|
||||
const data = editor.getData();
|
||||
setSummery(data);
|
||||
setSummary(data);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className="experience__content"
|
||||
dangerouslySetInnerHTML={{ __html: summery }}
|
||||
dangerouslySetInnerHTML={{ __html: summary }}
|
||||
></div>
|
||||
)}
|
||||
</div>
|
||||
|
@ -361,132 +361,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__executor {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e3e2e2;
|
||||
padding: 2px 6px;
|
||||
position: relative;
|
||||
max-width: 190px;
|
||||
width: 100%;
|
||||
|
||||
@media (max-width: 915px) {
|
||||
margin-right: 0;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
@media (max-width: 650px) {
|
||||
border-color: gray;
|
||||
}
|
||||
|
||||
&-selected {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 8px;
|
||||
max-width: 220px;
|
||||
width: 100%;
|
||||
margin-right: 10px;
|
||||
justify-content: center;
|
||||
|
||||
p {
|
||||
color: #252c32;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
max-width: 155px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.delete {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
img {
|
||||
display: flex;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 915px) {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
justify-content: start;
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
p {
|
||||
color: #252c32;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
img {
|
||||
transition: all 0.15s ease;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
&-dropdown {
|
||||
position: absolute;
|
||||
top: 33px;
|
||||
left: 0;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
z-index: 5;
|
||||
padding: 10px 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 7px;
|
||||
width: 100%;
|
||||
|
||||
.executor-dropdown__person {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
p {
|
||||
max-width: 155px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@media (max-width: 915px) {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
p {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__back {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
@ -517,211 +391,6 @@
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__tags {
|
||||
position: relative;
|
||||
img {
|
||||
transition: all 0.15s ease;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.tags {
|
||||
&__add {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 10px;
|
||||
column-gap: 5px;
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e3e2e2;
|
||||
max-height: 30px;
|
||||
|
||||
p {
|
||||
white-space: nowrap;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
}
|
||||
|
||||
span {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
border-radius: 50px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
background: #99b4f3;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
}
|
||||
&__list {
|
||||
position: absolute;
|
||||
border-radius: 8px;
|
||||
background: #d9d9d9;
|
||||
z-index: 8;
|
||||
top: 30px;
|
||||
left: -35px;
|
||||
width: 216px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@media (max-width: 900px) {
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.close {
|
||||
cursor: pointer;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
&__created {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 8px;
|
||||
margin-top: 8px;
|
||||
padding: 0 8px 8px;
|
||||
|
||||
.tag-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
column-gap: 5px;
|
||||
padding: 0px 8px;
|
||||
border-radius: 8px;
|
||||
height: 40px;
|
||||
max-height: 40px;
|
||||
background: #fff;
|
||||
|
||||
&__description {
|
||||
font-size: 12px;
|
||||
word-break: break-word;
|
||||
max-width: 115px;
|
||||
max-height: 40px;
|
||||
overflow: hidden;
|
||||
text-wrap: wrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&__color {
|
||||
width: 22.25px;
|
||||
height: 23.217px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
&__images {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
row-gap: 3px;
|
||||
|
||||
img {
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.delete {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 10px;
|
||||
|
||||
&__name {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-new-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 15px;
|
||||
border-radius: 8px;
|
||||
background: white;
|
||||
color: #252c32;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
justify-content: center;
|
||||
margin: 8px 8px 0px;
|
||||
|
||||
p {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
span {
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
border-radius: 50px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
background: #52b709;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.form-tag {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 8px;
|
||||
row-gap: 8px;
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 5px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
&__input {
|
||||
outline: none;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e3e2e2;
|
||||
font-size: 15px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
&__btn {
|
||||
outline: none;
|
||||
border: none;
|
||||
background: #252c32;
|
||||
color: whitesmoke;
|
||||
margin: 0 auto 0;
|
||||
border-radius: 10px;
|
||||
font-size: 15px;
|
||||
padding: 5px 12px;
|
||||
}
|
||||
|
||||
.disable {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__container {
|
||||
@ -757,372 +426,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__board {
|
||||
background: #f5f7f9;
|
||||
box-shadow: 0px 2px 5px rgba(60, 66, 87, 0.04),
|
||||
0px 0px 0px 1px rgba(60, 66, 87, 0.08),
|
||||
0px 1px 1px rgba(0, 0, 0, 0.06);
|
||||
border-radius: 8px;
|
||||
padding: 12px 10px 12px 8px;
|
||||
width: 360px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 10px;
|
||||
height: fit-content;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
transform: scaleY(-1);
|
||||
min-height: 815px;
|
||||
|
||||
@media (max-width: 900px) {
|
||||
min-width: auto;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
transform: scaleX(1);
|
||||
}
|
||||
|
||||
.tasks-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 8px;
|
||||
max-height: 750px;
|
||||
overflow: auto;
|
||||
padding: 5px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #cbd9f9;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #c5c0c6;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__hover {
|
||||
box-shadow: 0px 2px 10px #9cc480,
|
||||
0px 0px 0px 1px rgba(60, 66, 87, 0.08),
|
||||
0px 1px 1px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.task__hover {
|
||||
box-shadow: 0 0 5px gray;
|
||||
}
|
||||
|
||||
&__item {
|
||||
width: 328px;
|
||||
padding: 6px 10px 8px 10px;
|
||||
position: relative;
|
||||
box-shadow: 0px 3px 2px -2px rgba(0, 0, 0, 0.06),
|
||||
0px 5px 3px -2px rgba(0, 0, 0, 0.02);
|
||||
border-radius: 6px;
|
||||
background: #ffffff;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
transition: 0.4s;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.025);
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
width: 100%;
|
||||
max-height: none;
|
||||
|
||||
&:hover {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__hide {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&__title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
|
||||
p {
|
||||
color: #1a1919;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 0;
|
||||
max-height: 100px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 3;
|
||||
}
|
||||
|
||||
span {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
border-radius: 6px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
padding-bottom: 10px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 1px solid #dddddd;
|
||||
}
|
||||
}
|
||||
|
||||
&__description {
|
||||
margin: 4px 0;
|
||||
color: #5c6165;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 120%;
|
||||
max-height: 100px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
&__container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
column-gap: 10px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
pointer-events: none;
|
||||
margin-top: 5px;
|
||||
|
||||
&__tags {
|
||||
display: flex;
|
||||
column-gap: 5px;
|
||||
}
|
||||
|
||||
&__more {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
span {
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
color: #6e7c87;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
&__avatars {
|
||||
position: relative;
|
||||
img {
|
||||
position: relative;
|
||||
}
|
||||
img:first-child {
|
||||
right: -15px;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__priority {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 5px;
|
||||
margin-top: 3px;
|
||||
|
||||
p {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.high {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.middle {
|
||||
color: #cece00;
|
||||
}
|
||||
|
||||
.low {
|
||||
color: green;
|
||||
}
|
||||
}
|
||||
|
||||
&__dead-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 5px;
|
||||
|
||||
p {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: #1458dd;
|
||||
}
|
||||
|
||||
span {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
img {
|
||||
margin-top: -2px;
|
||||
width: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
&__executor {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
column-gap: 5px;
|
||||
|
||||
span {
|
||||
max-width: 210px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
&__tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 6px;
|
||||
row-gap: 3px;
|
||||
margin: 3px 0;
|
||||
|
||||
.tag-item {
|
||||
padding: 3px 10px;
|
||||
border-radius: 10px;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.open-items {
|
||||
cursor: pointer;
|
||||
border-radius: 44px;
|
||||
width: 33px;
|
||||
height: 33px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
bottom: -15px;
|
||||
font-size: 20px;
|
||||
left: 165px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.more-items {
|
||||
background: #8bcc60;
|
||||
}
|
||||
|
||||
.less-items {
|
||||
background: #f92828;
|
||||
}
|
||||
|
||||
&__more {
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
.column__select {
|
||||
position: absolute;
|
||||
padding: 15px;
|
||||
background: #e1fccf;
|
||||
border-radius: 12px;
|
||||
right: -20px;
|
||||
top: 5px;
|
||||
z-index: 7;
|
||||
row-gap: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@media (max-width: 910px) {
|
||||
right: 10px;
|
||||
top: 40px;
|
||||
}
|
||||
|
||||
&__item {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
img {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__no-items {
|
||||
font-weight: 500;
|
||||
font-size: 25px;
|
||||
transform: scaleY(-1);
|
||||
|
||||
@media (max-width: 900px) {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__no-tasks {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transform: scaleY(-1);
|
||||
|
||||
&-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
img {
|
||||
width: 27px;
|
||||
height: 27px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 700;
|
||||
font-size: 22px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
&-more {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.board {
|
||||
&__head {
|
||||
position: relative;
|
||||
@ -1388,7 +691,7 @@
|
||||
gap: 10px;
|
||||
|
||||
p {
|
||||
width: 55px;
|
||||
width: 63px;
|
||||
font-size: 12px;
|
||||
color: #5b6871;
|
||||
font-weight: 400;
|
||||
@ -1958,7 +1261,7 @@
|
||||
.table {
|
||||
&__search {
|
||||
display: flex;
|
||||
background: #F0F2F5;
|
||||
background: #f0f2f5;
|
||||
border-radius: 5px;
|
||||
width: 100%;
|
||||
padding: 14px 12px;
|
||||
@ -1976,11 +1279,11 @@
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 16px;
|
||||
color: #9BABC5;
|
||||
color: #9babc5;
|
||||
width: 100%;
|
||||
|
||||
&::placeholder {
|
||||
color: #9BABC5;
|
||||
color: #9babc5;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1995,12 +1298,12 @@
|
||||
width: 32px;
|
||||
border-radius: 5px;
|
||||
height: 32px;
|
||||
color: #2E3A59;
|
||||
color: #2e3a59;
|
||||
}
|
||||
|
||||
.switch {
|
||||
border: none;
|
||||
background: #F0F2F5;
|
||||
background: #f0f2f5;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@ -2016,12 +1319,12 @@
|
||||
background: white;
|
||||
|
||||
.page {
|
||||
border: 1px solid #E8ECF8;
|
||||
border: 1px solid #e8ecf8;
|
||||
background: none;
|
||||
|
||||
&--active {
|
||||
border: none;
|
||||
background: #9DA65D;
|
||||
background: #9da65d;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
@ -2029,17 +1332,20 @@
|
||||
}
|
||||
|
||||
table {
|
||||
grid-template-columns: minmax(0px, 2fr) minmax(0px, 1fr) minmax(0px, 1fr) minmax(0px, 1fr);
|
||||
grid-template-columns: minmax(0px, 2fr) minmax(0px, 1fr) minmax(0px, 1fr) minmax(
|
||||
0px,
|
||||
1fr
|
||||
);
|
||||
th {
|
||||
border-top: none;
|
||||
border-bottom: 1px solid #F5F6F8;
|
||||
color: #2E3A59;
|
||||
border-bottom: 1px solid #f5f6f8;
|
||||
color: #2e3a59;
|
||||
padding: 0 7.5px 15px;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 22px 7.5px;
|
||||
color: #2E3A59;
|
||||
color: #2e3a59;
|
||||
border-top: none;
|
||||
|
||||
p {
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
|
||||
import { getProfileInfo } from "@redux/outstaffingSlice";
|
||||
import { setEditReport } from "@redux/reportSlice";
|
||||
|
||||
import {
|
||||
@ -9,6 +10,7 @@ import {
|
||||
getCreatedDate,
|
||||
hourOfNum
|
||||
} from "@utils/calendarHelper";
|
||||
import { urlForLocal } from "@utils/helper";
|
||||
|
||||
import { apiRequest } from "@api/request";
|
||||
|
||||
@ -20,8 +22,9 @@ import { Navigation } from "@components/Navigation/Navigation";
|
||||
import { ProfileBreadcrumbs } from "@components/ProfileBreadcrumbs/ProfileBreadcrumbs";
|
||||
import { ProfileHeader } from "@components/ProfileHeader/ProfileHeader";
|
||||
|
||||
import arrowSwitchDate from "assets/icons/arrows/arrowViewReport.png";
|
||||
import arrowSwitchDate from "assets/icons/arrows/arrowCalendar_right.png";
|
||||
import arrow from "assets/icons/arrows/left-arrow.png";
|
||||
import avatarMok from "assets/images/avatarMok.png";
|
||||
|
||||
import "./viewReport.scss";
|
||||
|
||||
@ -31,6 +34,7 @@ export const ViewReport = () => {
|
||||
const [previousReportDay] = useState(new Date(params.date));
|
||||
const [nextReportDay] = useState(new Date(params.date));
|
||||
|
||||
const profileInfo = useSelector(getProfileInfo);
|
||||
const [taskText, setTaskText] = useState([]);
|
||||
const [difficulties, setDifficulties] = useState([]);
|
||||
const [tomorrowTask, setTomorrowTask] = useState([]);
|
||||
@ -140,6 +144,33 @@ export const ViewReport = () => {
|
||||
<img src={arrow} alt="#" />
|
||||
<p>Вернуться</p>
|
||||
</Link>
|
||||
<div className="summary__info">
|
||||
<div className="summary__person">
|
||||
<img
|
||||
src={
|
||||
profileInfo?.photo
|
||||
? urlForLocal(profileInfo.photo)
|
||||
: avatarMok
|
||||
}
|
||||
className="summary__avatar"
|
||||
alt="avatar"
|
||||
/>
|
||||
<p className="summary__name">
|
||||
{profileInfo?.fio || profileInfo?.username}{" "}
|
||||
{profileInfo.specification}
|
||||
</p>
|
||||
<hr />
|
||||
<div className="summary__direction">Front End</div>
|
||||
<div className="summary__level">Middle+</div>
|
||||
</div>
|
||||
<div className="summary__skill">
|
||||
<p>Ключевые навыки:</p>
|
||||
<div>Java</div>
|
||||
<div>Java</div>
|
||||
<div>Solid</div>
|
||||
<div>Java</div>
|
||||
</div>
|
||||
</div>
|
||||
{localStorage.getItem("role_status") !== "18" && (
|
||||
<div className="view-report__bar">
|
||||
<Link
|
||||
|
@ -4,6 +4,25 @@
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
|
||||
.summary__skill {
|
||||
color: #6f6f6f;
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
column-gap: 10px;
|
||||
|
||||
div {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
|
||||
border: #8dc63f 0.5px solid;
|
||||
border-radius: 44px;
|
||||
padding: 3px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1160px;
|
||||
display: flex;
|
||||
@ -45,13 +64,18 @@
|
||||
&__back {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 30px;
|
||||
column-gap: 10px;
|
||||
margin-top: 20px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 23px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
font-weight: 400;
|
||||
@ -68,7 +92,7 @@
|
||||
background: #ffffff;
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
padding: 20px 33px;
|
||||
padding: 55px;
|
||||
align-items: center;
|
||||
column-gap: 20px;
|
||||
height: 72px;
|
||||
|
57
src/pages/WelcomePage/WelcomePage.jsx
Normal file
@ -0,0 +1,57 @@
|
||||
import React, { useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
import { AuthHeader } from "@components/Common/AuthHeader/AuthHeader";
|
||||
import BaseButton from "@components/Common/BaseButton/BaseButton";
|
||||
import { Footer } from "@components/Common/Footer/Footer";
|
||||
import { Loader } from "@components/Common/Loader/Loader";
|
||||
import SideBar from "@components/SideBar/SideBar";
|
||||
|
||||
import ITguild from "assets/images/logo/ITguild.svg";
|
||||
|
||||
import "./welcomePage.scss";
|
||||
|
||||
export const WelcomePage = () => {
|
||||
const [loader, setLoader] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className="welcome-page">
|
||||
<AuthHeader />
|
||||
|
||||
<div className="welcome-page-component">
|
||||
<div className="welcome-page-component__title">
|
||||
<img src={ITguild}></img>
|
||||
<h1>
|
||||
Добро пожаловать в <span>ITGuild</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div className="welcome-page-component__text">
|
||||
<p>
|
||||
Создавайте и редактируйте задачи и проекты вместе с другими
|
||||
участниками
|
||||
<br /> команды. Сервис для работы с IT специалистами
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="button-box">
|
||||
{loader ? (
|
||||
<Loader />
|
||||
) : (
|
||||
<BaseButton
|
||||
onClick={() => {
|
||||
setLoader(true);
|
||||
navigate("/profile");
|
||||
}}
|
||||
styles="button-box__submit"
|
||||
>
|
||||
Начать
|
||||
</BaseButton>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<SideBar />
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
44
src/pages/WelcomePage/welcomePage.scss
Normal file
@ -0,0 +1,44 @@
|
||||
.welcome-page {
|
||||
overflow: hidden;
|
||||
background-color: #f1f1f1;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&-component {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
|
||||
&__title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-weight: 500;
|
||||
font-size: 30px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 45px;
|
||||
|
||||
img {
|
||||
width: 151px;
|
||||
margin: 70px 0 19px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
margin-bottom: 45px;
|
||||
}
|
||||
|
||||
.button-box__submit {
|
||||
width: 174px;
|
||||
height: 50px;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ import { RegistrationSetting } from "@pages/RegistrationSetting/RegistrationSett
|
||||
import { SingleReportPage } from "@pages/SingleReportPage/SingleReportPage";
|
||||
import { Stack } from "@pages/Stack/Stack";
|
||||
import { TrackerIntro } from "@pages/TrackerIntro/TrackerIntro";
|
||||
import { WelcomePage } from "@pages/WelcomePage/WelcomePage";
|
||||
|
||||
import { FreeDevelopers } from "@components/FreeDevelopers/FreeDevelopers";
|
||||
|
||||
@ -24,6 +25,7 @@ export const GuestPage = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route exact path="/auth" element={<Auth />} />
|
||||
<Route exact path="/welcome-page" element={<WelcomePage />} />
|
||||
<Route exact path="/stack" element={<Stack />} />
|
||||
<Route exact path="/" element={<Landing />} />
|
||||
<Route path="*" element={<Navigate to="/auth" replace />} />
|
||||
|