41 Commits

Author SHA1 Message Date
16a5a1f548 partner request design 2024-05-03 19:23:46 +03:00
a763c5a69a partner request design 2024-05-03 19:23:15 +03:00
a7c58b2609 tracker tasks 2024-05-03 14:26:36 +03:00
3b8ec8e100 tracker tasks 2024-05-03 14:26:09 +03:00
94136c97e6 ProfileCalendar 2024-05-01 14:54:53 +03:00
4810412bd7 layout of the outstaffing page 2024-05-01 14:53:28 +03:00
3f46d60720 fix candidate resume 2024-04-24 19:20:21 +03:00
d49a2eb0c8 Merge pull request 'landing' (#36) from landing into main
Reviewed-on: #36
2024-04-22 19:03:38 +03:00
8de343016f stack steps 2024-04-22 19:03:00 +03:00
470bf6ec67 stack steps 2024-04-22 19:02:17 +03:00
dea1eb6104 resolve 2024-04-22 17:03:14 +03:00
ee20b49993 fix total hours 2024-04-22 17:01:37 +03:00
0e04e19c1b total day hours 2024-04-22 17:00:37 +03:00
bd4bfacd66 Add Cards in Profile 2024-04-22 16:19:40 +03:00
1bdabd32bf Merge pull request 'landing' (#35) from landing into main
Reviewed-on: #35
2024-04-22 16:04:48 +03:00
2d861de330 Merge pull request 'fixed/components' (#34) from fixed/components into main
Reviewed-on: #34
2024-04-22 14:05:18 +03:00
60aa8c7301 Merge branch 'main' of https://git.itguild.info/apuc/guild_front into fixed/components 2024-04-18 18:11:40 +03:00
13df697614 stack projects 2024-04-18 16:42:54 +03:00
79299d1177 stack projects 2024-04-18 16:42:18 +03:00
68a2df23c4 Merge pull request 'landing' (#33) from landing into main
Reviewed-on: #33
2024-04-18 15:27:03 +03:00
291e6a4a5d landing footer 2024-04-17 18:58:54 +03:00
0d436e71e4 landing footer 2024-04-17 18:58:39 +03:00
7a55188904 stack 2024-04-17 18:54:45 +03:00
3e80159c44 stack 2024-04-17 18:51:27 +03:00
363d43e04c Merge pull request 'landing' (#32) from landing into main
Reviewed-on: #32
2024-04-16 17:04:03 +03:00
a1da184bb0 Merge branch 'main' into landing
# Conflicts:
#	src/pages/roles/GuestPage.jsx
2024-04-16 17:02:36 +03:00
adf6ff4a1d Start fix table 2024-04-15 22:34:33 +03:00
9ffb981b67 Merge pull request 'fixed/components' (#31) from fixed/components into main
Reviewed-on: #31
2024-04-15 21:11:57 +03:00
0860591d28 Merge branch 'main' of https://git.itguild.info/apuc/guild_front into fixed/components 2024-04-15 21:09:17 +03:00
4a20d5e4fa WelcomePage fix 2024-04-15 16:21:41 +03:00
1434792d42 Fixed tracker card 2024-04-11 22:24:08 +03:00
ade31b767a Merge branch 'main' of https://git.itguild.info/apuc/guild_front into fixed/components 2024-04-11 21:06:34 +03:00
fafab29d25 Add welcome to the registration 2024-04-11 16:22:03 +03:00
10ffb1f7f1 Create new component TrackerTagList 2024-04-09 21:09:06 +03:00
75cf13d945 Merge branch 'main' of https://git.itguild.info/apuc/guild_front into fixed/components 2024-04-08 21:17:56 +03:00
a3e39b75d8 Create new component 2024-04-08 21:05:33 +03:00
c532302f73 Delete new component 2024-04-05 22:09:20 +03:00
36bdaf15e5 Merge branch 'main' of https://git.itguild.info/apuc/guild_front into fixed/components 2024-04-05 21:35:19 +03:00
9f98a0fea2 start fix tracker 2024-04-02 20:49:52 +03:00
1e950e9de0 Merge branch 'main' of https://git.itguild.info/apuc/guild_front into fixed/components 2024-04-02 20:04:21 +03:00
3580e25457 Fixed modal 2024-04-01 20:41:29 +03:00
48 changed files with 2900 additions and 1618 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -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 {

View File

@ -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} &nbsp;{" "}
{currentCandidateObj.specification} &nbsp;{" "}
{SKILLS[currentCandidateObj.position_id]} &nbsp;{" "}
{LEVELS[currentCandidateObj.level]}

View File

@ -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>

View File

@ -38,7 +38,7 @@
font-weight: 500;
font-size: 18px;
line-height: 22px;
max-width: 125px;
max-width: 165px;
margin-bottom: 0;
}
}

View File

@ -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"
>

View File

@ -664,11 +664,8 @@ export const ModalTiсket = ({
"EasyImage",
"Image",
"ImageCaption",
"ImageStyle",
"ImageToolbar",
"ImageUpload",
"MediaEmbed",
"BlockQuote"
"MediaEmbed"
]
}}
onChange={(event, editor) => {

View File

@ -683,11 +683,8 @@ export const TicketFullScreen = () => {
"EasyImage",
"Image",
"ImageCaption",
"ImageStyle",
"ImageToolbar",
"ImageUpload",
"MediaEmbed",
"BlockQuote"
"MediaEmbed"
]
}}
onChange={(event, editor) => {

View File

@ -624,7 +624,15 @@ export const TrackerModal = ({
"bulletedList",
"numberedList"
],
removePlugins: ["BlockQuote"],
removePlugins: [
"CKFinderUploadAdapter",
"CKFinder",
"EasyImage",
"Image",
"ImageCaption",
"ImageUpload",
"MediaEmbed"
],
placeholder: "Описание задачи"
}}
onChange={(event, editor) => {

View File

@ -719,7 +719,6 @@
.button-add {
margin: 0 30px;
align-self: flex-end;
}
}

View File

@ -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>
))

View File

@ -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;

View 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;

View 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;
}
}
}
}

View 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;

View 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;
}
}
}
}
}
}
}

View 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;

View 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;
}
}
}
}
}
}
}

View File

@ -127,11 +127,8 @@ export const TrackerTaskComment = ({
"EasyImage",
"Image",
"ImageCaption",
"ImageStyle",
"ImageToolbar",
"ImageUpload",
"MediaEmbed",
"BlockQuote"
"MediaEmbed"
]
}}
onChange={(event, editor) => {

View File

@ -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>
);

View File

@ -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;
}
}
}
}
}

View File

@ -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>

View File

@ -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;

View File

@ -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";

View File

@ -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

View File

@ -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;

View File

@ -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: [

View File

@ -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>

View File

@ -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>
);

View File

@ -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 {
}
}
}
}

View File

@ -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>

View File

@ -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 {

View File

@ -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

View File

@ -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;

View 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>
);
};

View 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;
}
}
}

View File

@ -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 />} />