Fixed full components

This commit is contained in:
MaxOvs19 2023-05-24 19:23:24 +03:00
parent 70b3845755
commit f88b22cb84
40 changed files with 1362 additions and 1096 deletions

View File

@ -1,103 +0,0 @@
import React, { useEffect, useState } from 'react'
import { NavLink } from 'react-router-dom'
import { urlForLocal } from '../../helper'
import { apiRequest } from '../../api/request';
import { useDispatch, useSelector } from 'react-redux';
import { getProfileInfo, setProfileInfo } from '../../redux/outstaffingSlice';
import avatarMok from "../../pages/PartnerTreaties/Images/avatarMok.png"
export const Navigation = () => {
const dispatch = useDispatch();
const profileInfo = useSelector(getProfileInfo);
const [user] = useState(localStorage.getItem('role_status') === '18' ? 'partner' : 'developer')
const [navInfo] = useState({
developer: [
{
path: '/summary',
name: 'Резюме'
},
{
path: '/calendar',
name: 'Отчетность'
},
{
path: '/tracker',
name: 'Трекер'
},
{
path: '/payouts',
name: 'Выплаты'
},
{
path: '/settings',
name: 'Настройки'
},
],
partner: [
{
path: '/catalog',
name: 'Каталог'
},
{
path: '/requests',
name: 'Запросы'
},
{
path: '/categories',
name: 'Персонал'
},
{
path: '/tracker',
name: 'Трекер'
},
{
path: '/treaties',
name: 'Договора'
},
{
path: '/settings',
name: 'Настройки'
},
]
})
useEffect(() => {
if (localStorage.getItem('role_status') === '18') {
return
}
apiRequest(`/profile/${localStorage.getItem('cardId')}`)
.then((profileInfo) =>
dispatch(setProfileInfo(profileInfo))
);
}, [dispatch]);
return (
<div className='profileHeader__info'>
<div className='profileHeader__container'>
<nav className='profileHeader__nav'>
{
navInfo[user].map((link, index) => {
return <NavLink key={index} end to={`/profile${link.path}`}>{link.name}</NavLink>
})
}
</nav>
<div className='profileHeader__personalInfo'>
<h3 className='profileHeader__personalInfoName'>
{user === 'developer' ?
profileInfo?.fio :
''
}
</h3>
<NavLink end to={'/profile'}>
<img src={profileInfo.photo ? urlForLocal(profileInfo.photo) : avatarMok} className='profileHeader__personalInfoAvatar' alt='avatar' />
</NavLink>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,107 @@
import React, { useEffect, useState } from "react";
import { NavLink } from "react-router-dom";
import { urlForLocal } from "../../helper";
import { apiRequest } from "../../api/request";
import { useDispatch, useSelector } from "react-redux";
import { getProfileInfo, setProfileInfo } from "../../redux/outstaffingSlice";
import avatarMok from "../../pages/PartnerTreaties/Images/avatarMok.png";
export const Navigation = () => {
const dispatch = useDispatch();
const profileInfo = useSelector(getProfileInfo);
const [user] = useState(
localStorage.getItem("role_status") === "18" ? "partner" : "developer"
);
const [navInfo] = useState({
developer: [
{
path: "/summary",
name: "Резюме",
},
{
path: "/calendar",
name: "Отчетность",
},
{
path: "/tracker",
name: "Трекер",
},
{
path: "/payouts",
name: "Выплаты",
},
{
path: "/settings",
name: "Настройки",
},
],
partner: [
{
path: "/catalog",
name: "Каталог",
},
{
path: "/requests",
name: "Запросы",
},
{
path: "/categories",
name: "Персонал",
},
{
path: "/tracker",
name: "Трекер",
},
{
path: "/treaties",
name: "Договора",
},
{
path: "/settings",
name: "Настройки",
},
],
});
useEffect(() => {
if (localStorage.getItem("role_status") === "18") {
return;
}
apiRequest(`/profile/${localStorage.getItem("cardId")}`).then(
(profileInfo) => dispatch(setProfileInfo(profileInfo))
);
}, [dispatch]);
return (
<div className="profileHeader__info">
<div className="profileHeader__container">
<nav className="profileHeader__nav">
{navInfo[user].map((link, index) => {
return (
<NavLink key={index} end to={`/profile${link.path}`}>
{link.name}
</NavLink>
);
})}
</nav>
<div className="profileHeader__personalInfo">
<h3 className="profileHeader__personalInfoName">
{user === "developer" ? profileInfo?.fio : ""}
</h3>
<NavLink end to={"/profile"}>
<img
src={
profileInfo.photo ? urlForLocal(profileInfo.photo) : avatarMok
}
className="profileHeader__personalInfoAvatar"
alt="avatar"
/>
</NavLink>
</div>
</div>
</div>
);
};

View File

@ -1,61 +0,0 @@
import React, {useEffect, useState} from 'react';
import {useNavigate, NavLink} from "react-router-dom";
import {useDispatch, useSelector} from "react-redux";
import {Loader} from '../Loader/Loader'
import {apiRequest} from "../../api/request";
import {auth, setProfileInfo} from "../../redux/outstaffingSlice";
import {getRole} from "../../redux/roleSlice";
import './profileHeader.scss'
export const ProfileHeader = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const userRole = useSelector(getRole);
const [user] = useState(localStorage.getItem('role_status') === '18' ? 'partner' : 'developer')
const [isLoggingOut, setIsLoggingOut] = useState(false);
useEffect(() => {
if (localStorage.getItem('role_status') === '18') {
return
}
apiRequest(`/profile/${localStorage.getItem('cardId')}`)
.then((profileInfo) =>
dispatch(setProfileInfo(profileInfo))
);
}, [dispatch]);
const handler = () => {
setIsLoggingOut(true);
localStorage.clear();
dispatch(auth(false));
setIsLoggingOut(false);
navigate(userRole === 'ROLE_DEV' ? '/authdev' : '/auth')
};
return (
<header className='profileHeader'>
<div className='profileHeader__head'>
<div className='profileHeader__container'>
<NavLink to={'/profile'} className='profileHeader__title'>itguild.
<span>
{user === 'developer' ?
'для разработчиков' :
'для партнеров'
}
</span>
</NavLink>
<button onClick={handler} className='profileHeader__logout'>
{isLoggingOut ? <Loader/> : 'Выйти'}
</button>
</div>
</div>
</header>
)
};

View File

@ -0,0 +1,57 @@
import React, { useEffect, useState } from "react";
import { useNavigate, NavLink } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { Loader } from "../Loader/Loader";
import { apiRequest } from "../../api/request";
import { auth, setProfileInfo } from "../../redux/outstaffingSlice";
import { getRole } from "../../redux/roleSlice";
import "./profileHeader.scss";
export const ProfileHeader = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const userRole = useSelector(getRole);
const [user] = useState(
localStorage.getItem("role_status") === "18" ? "partner" : "developer"
);
const [isLoggingOut, setIsLoggingOut] = useState(false);
useEffect(() => {
if (localStorage.getItem("role_status") === "18") {
return;
}
apiRequest(`/profile/${localStorage.getItem("cardId")}`).then(
(profileInfo) => dispatch(setProfileInfo(profileInfo))
);
}, [dispatch]);
const handler = () => {
setIsLoggingOut(true);
localStorage.clear();
dispatch(auth(false));
setIsLoggingOut(false);
navigate(userRole === "ROLE_DEV" ? "/authdev" : "/auth");
};
return (
<header className="profileHeader">
<div className="profileHeader__head">
<div className="profileHeader__container">
<NavLink to={"/profile"} className="profileHeader__title">
itguild.
<span>
{user === "developer" ? "для разработчиков" : "для партнеров"}
</span>
</NavLink>
<button onClick={handler} className="profileHeader__logout">
{isLoggingOut ? <Loader /> : "Выйти"}
</button>
</div>
</div>
</header>
);
};

View File

@ -2,10 +2,10 @@
width: 100%;
display: flex;
flex-direction: column;
font-family: 'LabGrotesque', sans-serif;
font-family: "LabGrotesque", sans-serif;
&__head {
background: #E1FCCF;
background: #e1fccf;
}
&__container {
@ -26,7 +26,7 @@
color: black;
span {
color: #52B709;
color: #52b709;
}
&:hover {
@ -45,7 +45,7 @@
}
&__info {
background: #FFFFFF;
background: #ffffff;
}
&__nav {

View File

@ -13,20 +13,19 @@ registerLocale("ru", ru);
import { Loader } from "../Loader/Loader";
import { Footer } from "../Footer/Footer";
import { ProfileHeader } from "../ProfileHeader/ProfileHeader";
import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs";
import { ProfileBreadcrumbs } from "../ProfileBreadcrumbs/ProfileBreadcrumbs";
import { apiRequest } from "../../api/request";
import { Navigation } from "../Navigation/Navigation";
import { getReportDate } from "../../redux/reportSlice";
import calendarIcon from "../../images/calendar.svg";
import ellipse from "../../images/ellipse.png";
import remove from "../../images/remove.png";
import arrow from "../../images/right-arrow.png";
import remove from "../../images/remove.svg";
import arrow from "../../images/left-arrow.png";
import "./reportForm.scss";
import "react-datepicker/dist/react-datepicker.css";
import { Navigation } from "../Navigation/Navigation";
const ReportForm = () => {
if (localStorage.getItem("role_status") === "18") {

View File

@ -1,86 +0,0 @@
.search {
margin-top: 73px;
}
.search__title {
font-family: 'GT Eesti Pro Display', sans-serif;
font-size: 2.4em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: center;
margin-bottom: 40px;
}
.search__box {
display: flex;
justify-content: space-between;
}
@media (max-width: 575.98px) {
.search__box {
flex-direction: column;
align-items: center;
}
}
.search__box > button {
color: white;
width: 131px;
height: 40px;
border-radius: 10px;
border: none;
font-family: 'Muller', sans-serif;
font-size: 1.6em;
letter-spacing: 0.8px;
text-align: center;
background-color: #73c141;
}
.search__box > button:hover {
background: rgba(0, 0, 0, 0);
color: #73c141;
box-shadow: inset 0 0 0 3px #73c141;
}
@media (max-width: 575.98px) {
.search__box > button {
margin-top: 20px;
}
}
.select {
width: 85%;
}
[class$='-placeholder'] {
display: none;
}
[class$='-control'] {
border-color: #e8e8e8 !important;
box-shadow: 0 0 0 0 #e8e8e8 !important;
}
[class$='-value__label'] {
font-size: 1.4em !important;
}
[class$='-option'] {
font-size: 1.6em !important;
color: gray !important;
}
.search__submit {
font-weight: bold;
}
.search__submit:hover .loader path {
fill: #6aaf5c;
}
@media (max-width: 990px) {
.select {
margin-right: 1rem;
}
}

View File

@ -1,86 +0,0 @@
import React, {useState} from 'react'
import {useSelector, useDispatch} from 'react-redux'
import Select from 'react-select'
import {Loader} from '../Loader/Loader'
import {apiRequest} from "../../api/request";
import {
selectedItems,
selectItems,
selectTags,
profiles,
setPositionId
} from '../../redux/outstaffingSlice'
import './TagSelect.css'
const TagSelect = () => {
const [searchLoading, setSearchLoading] = useState(false);
const dispatch = useDispatch();
const itemsArr = useSelector(selectItems);
const tagsArr = useSelector(selectTags);
const handleSubmit = ({dispatch, setSearchLoading}) => {
setSearchLoading(true);
dispatch(setPositionId(null));
const filterItemsId = itemsArr.map((item) => item.id).join();
const params = filterItemsId ? {skill: filterItemsId} : '';
apiRequest('/profile', {
params: {...params, limit: 1000},
}).then((res) => {
dispatch(profiles(res));
setSearchLoading(false)
})
// dispatch(selectedItems([]));
};
return (
<>
<section className='search'>
<div className='row'>
<div className='col-12'>
<h2 className='search__title'>
Найти специалиста по навыкам
</h2>
<div className='search__box'>
<Select
value={itemsArr}
onChange={(value) => dispatch(selectedItems(value))}
isMulti
name='tags'
className='select'
classNamePrefix='select'
options={
tagsArr &&
tagsArr.flat().map((item) => {
return {
id: item.id,
value: item.value,
label: item.value
}
})
}
/>
<button
onClick={() => handleSubmit({dispatch, setSearchLoading})}
type='submit'
className='search__submit'
>
{searchLoading ? <Loader width={30} height={30}/> : 'Поиск'}
</button>
</div>
</div>
</div>
</section>
</>
)
};
export default TagSelect

View File

@ -0,0 +1,79 @@
import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import Select from "react-select";
import { Loader } from "../Loader/Loader";
import { apiRequest } from "../../api/request";
import {
selectedItems,
selectItems,
selectTags,
profiles,
setPositionId,
} from "../../redux/outstaffingSlice";
import "./tagSelect.scss";
const TagSelect = () => {
const [searchLoading, setSearchLoading] = useState(false);
const dispatch = useDispatch();
const itemsArr = useSelector(selectItems);
const tagsArr = useSelector(selectTags);
const handleSubmit = ({ dispatch, setSearchLoading }) => {
setSearchLoading(true);
dispatch(setPositionId(null));
const filterItemsId = itemsArr.map((item) => item.id).join();
const params = filterItemsId ? { skill: filterItemsId } : "";
apiRequest("/profile", {
params: { ...params, limit: 1000 },
}).then((res) => {
dispatch(profiles(res));
setSearchLoading(false);
});
};
return (
<>
<section className="search">
<div className="row">
<div className="col-12">
<h2 className="search__title">Найти специалиста по навыкам</h2>
<div className="search__box">
<Select
value={itemsArr}
onChange={(value) => dispatch(selectedItems(value))}
isMulti
name="tags"
className="select"
classNamePrefix="select"
options={
tagsArr &&
tagsArr.flat().map((item) => {
return {
id: item.id,
value: item.value,
label: item.value,
};
})
}
/>
<button
onClick={() => handleSubmit({ dispatch, setSearchLoading })}
type="submit"
className="search__submit"
>
{searchLoading ? <Loader width={30} height={30} /> : "Поиск"}
</button>
</div>
</div>
</div>
</section>
</>
);
};
export default TagSelect;

View File

@ -0,0 +1,81 @@
.search {
margin-top: 73px;
&__title {
font-family: "GT Eesti Pro Display", sans-serif;
font-size: 2.4em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: center;
margin-bottom: 40px;
}
&__box {
display: flex;
justify-content: space-between;
button {
color: white;
width: 131px;
height: 40px;
border-radius: 10px;
border: none;
font-family: "Muller", sans-serif;
font-size: 1.6em;
letter-spacing: 0.8px;
text-align: center;
background-color: #73c141;
&:hover {
background: rgba(0, 0, 0, 0);
color: #73c141;
box-shadow: inset 0 0 0 3px #73c141;
}
@media (max-width: 575.98px) {
margin-top: 20px;
}
}
@media (max-width: 575.98px) {
flex-direction: column;
align-items: center;
}
}
&__submit {
font-weight: bold;
}
.select {
width: 85%;
@media (max-width: 990px) {
margin-right: 1rem;
}
}
}
[class$="-placeholder"] {
display: none;
}
[class$="-control"] {
border-color: #e8e8e8 !important;
box-shadow: 0 0 0 0 #e8e8e8 !important;
}
[class$="-value__label"] {
font-size: 1.4em !important;
}
[class$="-option"] {
font-size: 1.6em !important;
color: gray !important;
}
.search__submit:hover .loader path {
fill: #6aaf5c;
}

View File

@ -1,4 +1,6 @@
import React, { useEffect, useState } from "react";
import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Link } from "react-router-dom";
import { apiRequest } from "../../api/request";
import {
@ -11,8 +13,6 @@ import {
getSendRequest,
setSendRequest,
} from "../../redux/reportSlice";
import { useDispatch, useSelector } from "react-redux";
import { Link } from "react-router-dom";
import { Loader } from "../Loader/Loader";
import "./shortReport.scss";

View File

@ -1,12 +1,11 @@
import React, { useState } from "react";
import Slider from "react-slick";
import { Link } from "react-router-dom";
import mockWorker from "../../images/mokPerson.png";
import "./sliderWorkers.scss";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import { Link } from "react-router-dom";
export const SliderWorkers = ({ title, titleInfo, subTitle }) => {
const [workers] = useState([

View File

@ -1,44 +1,85 @@
import React, { useEffect, useState } from 'react'
import './StarRating.scss'
import React, { useEffect, useState } from "react";
import "./StarRating.scss";
const StarRating = ({ countStars=1, countActiveStars=1, color='#52B709', size=61 }) => {
const [shadedStars, setShadedStars] = useState([])
const [noShadedStars, setNoShadedStars] = useState([])
const percent = (Math.abs(countActiveStars)) >= countStars ? 100 : (countActiveStars * 100 / countStars)
const StarRating = ({
countStars = 1,
countActiveStars = 1,
color = "#52B709",
size = 61,
}) => {
const [shadedStars, setShadedStars] = useState([]);
const [noShadedStars, setNoShadedStars] = useState([]);
const percent =
Math.abs(countActiveStars) >= countStars
? 100
: (countActiveStars * 100) / countStars;
useEffect(() => {
for (let index = 0; index < countStars; index++) {
setShadedStars(prev => [...prev, '★'])
setNoShadedStars(prev => [...prev, '☆'])
setShadedStars((prev) => [...prev, "★"]);
setNoShadedStars((prev) => [...prev, "☆"]);
}
}, [])
}, []);
const ratingStyle = {
"--size": size+'px'
}
"--size": size + "px",
};
const activeStyle = {
"--width": percent + '%',
"--width": percent + "%",
"--color": color,
"--content": shadedStars.join('')
}
"--content": shadedStars.join(""),
};
const bodyStyle = {
"--content": noShadedStars.join(''),
"--color": color
}
"--content": noShadedStars.join(""),
"--color": color,
};
return (
<div className='rating' style={ratingStyle}>
<div className="rating__body" style={bodyStyle} data-content={noShadedStars.join('')}>
<div className="rating__active" style={activeStyle} data-content={shadedStars.join('')}></div>
<div className="rating" style={ratingStyle}>
<div
className="rating__body"
style={bodyStyle}
data-content={noShadedStars.join("")}
>
<div
className="rating__active"
style={activeStyle}
data-content={shadedStars.join("")}
></div>
<div className="rating__items">
<input type='radio' className="rating__item" value={1} name='star'></input>
<input type='radio' className="rating__item" value={2} name='star'></input>
<input type='radio' className="rating__item" value={3} name='star'></input>
<input type='radio' className="rating__item" value={4} name='star'></input>
<input type='radio' className="rating__item" value={5} name='star'></input>
<input
type="radio"
className="rating__item"
value={1}
name="star"
></input>
<input
type="radio"
className="rating__item"
value={2}
name="star"
></input>
<input
type="radio"
className="rating__item"
value={3}
name="star"
></input>
<input
type="radio"
className="rating__item"
value={4}
name="star"
></input>
<input
type="radio"
className="rating__item"
value={5}
name="star"
></input>
</div>
</div>
</div>
)
}
);
};
export default React.memo(StarRating);

View File

@ -42,4 +42,3 @@
opacity: 0;
}
}

View File

@ -1,20 +0,0 @@
import React from 'react';
import './stepForCandidate.scss'
export const StepsForCandidate = ({step}) => {
return(
<div className='step'>
<div className='step__start'>
<span>2</span>
<p>шага для твоего входа в команду </p>
</div>
<div className='step__info'>
<p>{step}</p>
<span>из 2</span>
</div>
</div>
)
};
export default StepsForCandidate

View File

@ -0,0 +1,20 @@
import React from "react";
import "./stepForCandidate.scss";
export const StepsForCandidate = ({ step }) => {
return (
<div className="step">
<div className="step__start">
<span>2</span>
<p>шага для твоего входа в команду </p>
</div>
<div className="step__info">
<p>{step}</p>
<span>из 2</span>
</div>
</div>
);
};
export default StepsForCandidate;

View File

@ -20,7 +20,7 @@
font-weight: 900;
font-size: 258px;
line-height: 32px;
color: #52B709;
color: #52b709;
}
p {
@ -41,7 +41,7 @@
display: flex;
align-items: center;
p {
background: #DDEEC6;
background: #ddeec6;
border-radius: 44px;
padding: 8px 26px;
font-weight: 400;

View File

@ -1,16 +0,0 @@
import React from 'react'
import './taskItem.scss'
export const TaskItem = ({ index, text, hours }) => {
return (
<div className='task-item'>
<div className='task-item__index'>{index}.</div>
<div className='task-item__text'>{text}</div>
<div className='task-item__hours'>
<div className='task-item__hours-value'>{hours}</div>
<div className='task-item__hours-text'>Количество часов</div>
</div>
</div>
)
}

View File

@ -0,0 +1,16 @@
import React from "react";
import "./taskItem.scss";
export const TaskItem = ({ index, text, hours }) => {
return (
<div className="task-item">
<div className="task-item__index">{index}.</div>
<div className="task-item__text">{text}</div>
<div className="task-item__hours">
<div className="task-item__hours-value">{hours}</div>
<div className="task-item__hours-text">Количество часов</div>
</div>
</div>
);
};

View File

@ -6,7 +6,7 @@
&__index {
margin-top: 0.2rem;
color: #282828;
font-family: 'GT Eesti Pro Display';
font-family: "GT Eesti Pro Display";
font-size: 20px;
font-weight: 700;
line-height: 48.74px;
@ -19,7 +19,7 @@
max-width: 525px;
margin-left: 1.6rem;
color: #000000;
font-family: 'GT Eesti Pro Display';
font-family: "GT Eesti Pro Display";
font-size: 15px;
font-weight: 400;
letter-spacing: normal;
@ -49,7 +49,7 @@
);
color: #ffffff;
font-family: 'Muller Extra Bold';
font-family: "Muller Extra Bold";
font-size: 16px;
font-weight: 400;
text-align: left;
@ -61,7 +61,7 @@
width: 69px;
height: 25px;
color: #000000;
font-family: 'GT Eesti Pro Display - Thin';
font-family: "GT Eesti Pro Display - Thin";
font-size: 13px;
font-weight: 400;
letter-spacing: normal;

View File

@ -1,7 +1,8 @@
import React from "react";
import cardCalendar from "../../../images/cardCalendar.svg";
import { Link } from "react-router-dom";
import cardCalendar from "../../../images/cardCalendar.svg";
import "./cardArticle.scss";
export const CardArticle = ({ images, title, data, id }) => {

View File

@ -4,8 +4,8 @@ import { Link } from "react-router-dom";
import ModalLayout from "../ModalLayout/ModalLayout";
import avatar from "../../../images/mokPerson.png";
import logoTg from "../../../images/TgLogo.svg";
import arrow from "../../../images/right-arrow.png";
import logoTg from "../../../images/tgLogo.svg";
import arrow from "../../../images/left-arrow.png";
import interview from "../../../images/interviewLogo.svg";
import "./modalAspt.scss";

View File

@ -1,6 +1,6 @@
import React from "react";
import telegramLogo from "../../../images/TgLogo.svg";
import telegramLogo from "../../../images/tgLogo.svg";
import doc from "../../../images/doc.svg";
import anyMoment from "../../../images/anyMoment.svg";

View File

@ -1,17 +1,18 @@
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import TrackerModal from "../TrackerModal/TrackerModal";
import { apiRequest } from "../../../api/request";
import { useDispatch } from "react-redux";
import { urlForLocal } from "../../../helper";
import {
modalToggle,
setProjectBoardFetch,
} from "../../../redux/projectsTrackerSlice";
import { getCorrectDate } from "../../../components/Calendar/calendarHelper";
import {getCorrectDate} from '../../../components/Calendar/calendarHelper'
import category from "../../../images/category.png";
import watch from "../../../images/watch.png";
import category from "../../../images/category.svg";
import watch from "../../../images/watch.svg";
import file from "../../../images/fileModal.svg";
import arrow from "../../../images/arrowStart.png";
import link from "../../../images/link.svg";
@ -24,7 +25,6 @@ import fullScreen from "../../../images/inFullScreen.svg";
import close from "../../../images/closeProjectPersons.svg";
import "./ModalTicket.scss";
import {urlForLocal} from "../../../helper";
export const ModalTiсket = ({
active,
@ -32,20 +32,24 @@ export const ModalTiсket = ({
task,
projectId,
projectName,
projectUsers
projectUsers,
}) => {
const dispatch = useDispatch();
const [addSubtask, setAddSubtask] = useState(false);
const [editOpen, setEditOpen] = useState(false);
const [inputsValue, setInputsValue] = useState({title: task.title, description: task.description, comment: ''});
const [inputsValue, setInputsValue] = useState({
title: task.title,
description: task.description,
comment: "",
});
const [comments, setComments] = useState([]);
const [commentsEditOpen, setCommentsEditOpen] = useState({})
const [commentsEditText, setCommentsEditText] = useState({})
const [dropListOpen, setDropListOpen] = useState(false)
const [dropListMembersOpen, setDropListMembersOpen] = useState(false)
const [executor, setExecutor] = useState(task.executor)
const [members, setMembers] = useState(task.taskUsers)
const [users, setUsers] = useState([])
const [commentsEditOpen, setCommentsEditOpen] = useState({});
const [commentsEditText, setCommentsEditText] = useState({});
const [dropListOpen, setDropListOpen] = useState(false);
const [dropListMembersOpen, setDropListMembersOpen] = useState(false);
const [executor, setExecutor] = useState(task.executor);
const [members, setMembers] = useState(task.taskUsers);
const [users, setUsers] = useState([]);
function deleteTask() {
apiRequest("/task/update-task", {
@ -66,7 +70,7 @@ export const ModalTiсket = ({
data: {
task_id: task.id,
title: inputsValue.title,
description: inputsValue.description
description: inputsValue.description,
},
}).then((res) => {
dispatch(setProjectBoardFetch(projectId));
@ -79,39 +83,42 @@ export const ModalTiсket = ({
data: {
text: inputsValue.comment,
entity_type: 2,
entity_id: task.id
}
entity_id: task.id,
},
}).then((res) => {
let newComment = res
newComment.created_at = new Date()
setInputsValue((prevValue) => ({...prevValue, comment: ''}))
setComments((prevValue) => ([...prevValue, newComment]))
setCommentsEditOpen((prevValue) => ({...prevValue, [res.id]: false}))
setCommentsEditText((prevValue) => ({...prevValue, [res.id]: res.text}))
})
let newComment = res;
newComment.created_at = new Date();
setInputsValue((prevValue) => ({ ...prevValue, comment: "" }));
setComments((prevValue) => [...prevValue, newComment]);
setCommentsEditOpen((prevValue) => ({ ...prevValue, [res.id]: false }));
setCommentsEditText((prevValue) => ({
...prevValue,
[res.id]: res.text,
}));
});
}
function deleteComment(commentId) {
apiRequest("/comment/update", {
method: "PUT",
data: {
comment_id: commentId,
status: 0
}
status: 0,
},
}).then((res) => {
setComments((prevValue) => prevValue.filter((item) => item.id !== commentId))
})
setComments((prevValue) =>
prevValue.filter((item) => item.id !== commentId)
);
});
}
function editComment(commentId) {
apiRequest("/comment/update", {
method: "PUT",
data: {
comment_id: commentId,
text: commentsEditText[commentId]
}
}).then((res) => {
})
text: commentsEditText[commentId],
},
}).then((res) => {});
}
function taskExecutor(person) {
@ -119,11 +126,11 @@ export const ModalTiсket = ({
method: "PUT",
data: {
task_id: task.id,
executor_id: person.user_id
executor_id: person.user_id,
},
}).then((res) => {
setDropListOpen(false)
setExecutor(res.executor)
setDropListOpen(false);
setExecutor(res.executor);
});
}
@ -132,10 +139,10 @@ export const ModalTiсket = ({
method: "PUT",
data: {
task_id: task.id,
executor_id: 0
executor_id: 0,
},
}).then((res) => {
setExecutor(null)
setExecutor(null);
});
}
@ -144,11 +151,11 @@ export const ModalTiсket = ({
method: "POST",
data: {
task_id: task.id,
user_id: person.user_id
user_id: person.user_id,
},
}).then((res) => {
setDropListMembersOpen(false)
setMembers((prevValue) => ([...prevValue, res]))
setDropListMembersOpen(false);
setMembers((prevValue) => [...prevValue, res]);
});
}
@ -157,31 +164,40 @@ export const ModalTiсket = ({
method: "DELETE",
data: {
task_id: task.id,
user_id: person.user_id
user_id: person.user_id,
},
}).then((res) => {
setMembers(members.filter((item) => item.user_id !== person.user_id))
setMembers(members.filter((item) => item.user_id !== person.user_id));
});
}
useEffect(() => {
apiRequest(`/comment/get-by-entity?entity_type=2&entity_id=${task.id}`).then((res) => {
setComments(res)
apiRequest(
`/comment/get-by-entity?entity_type=2&entity_id=${task.id}`
).then((res) => {
setComments(res);
res.forEach((item) => {
setCommentsEditOpen((prevValue) => ({...prevValue, [item.id]: false}))
setCommentsEditText((prevValue) => ({...prevValue, [item.id]: item.text}))
})
})
}, [])
setCommentsEditOpen((prevValue) => ({
...prevValue,
[item.id]: false,
}));
setCommentsEditText((prevValue) => ({
...prevValue,
[item.id]: item.text,
}));
});
});
}, []);
useEffect(() => {
let ids = members.map((user) => user.user_id)
setUsers(projectUsers.reduce((acc, cur) => {
if (!ids.includes(cur.user_id)) acc.push(cur)
return acc
}, []))
}, [members])
let ids = members.map((user) => user.user_id);
setUsers(
projectUsers.reduce((acc, cur) => {
if (!ids.includes(cur.user_id)) acc.push(cur);
return acc;
}, [])
);
}, [members]);
return (
<div
@ -206,13 +222,33 @@ export const ModalTiсket = ({
<div className="content__task">
<span>Задача</span>
{editOpen ? <input value={inputsValue.title} onChange={(e) => {
setInputsValue((prevValue) => ({...prevValue, title: e.target.value}))
}} /> :<h5>{inputsValue.title}</h5>}
{editOpen ? (
<input
value={inputsValue.title}
onChange={(e) => {
setInputsValue((prevValue) => ({
...prevValue,
title: e.target.value,
}));
}}
/>
) : (
<h5>{inputsValue.title}</h5>
)}
<div className="content__description">
{editOpen ? <input value={inputsValue.description} onChange={(e) => {
setInputsValue((prevValue) => ({...prevValue, description: e.target.value}))
}}/> :<p>{inputsValue.description}</p>}
{editOpen ? (
<input
value={inputsValue.description}
onChange={(e) => {
setInputsValue((prevValue) => ({
...prevValue,
description: e.target.value,
}));
}}
/>
) : (
<p>{inputsValue.description}</p>
)}
{/*<img src={taskImg} className="image-task"></img>*/}
</div>
<div className="content__communication">
@ -237,97 +273,163 @@ export const ModalTiсket = ({
</p>
</div>
<div className="content__input">
<input placeholder="Оставить комментарий" value={inputsValue.comment} onChange={(e) => {
setInputsValue((prevValue) => ({...prevValue, comment: e.target.value}))
}} />
<input
placeholder="Оставить комментарий"
value={inputsValue.comment}
onChange={(e) => {
setInputsValue((prevValue) => ({
...prevValue,
comment: e.target.value,
}));
}}
/>
<img src={send} onClick={createComment}></img>
</div>
<div className='comments__list'>
<div className="comments__list">
{comments.map((comment) => {
return <div className='comments__list__item' key={comment.id}>
<div className='comments__list__item__info'>
return (
<div className="comments__list__item" key={comment.id}>
<div className="comments__list__item__info">
<span>{getCorrectDate(comment.created_at)}</span>
<div className={commentsEditOpen[comment.id] ? 'edit edit__open' : 'edit'} >
<img src={edit} alt='edit' onClick={() => {
<div
className={
commentsEditOpen[comment.id]
? "edit edit__open"
: "edit"
}
>
<img
src={edit}
alt="edit"
onClick={() => {
if (commentsEditOpen[comment.id]) {
editComment(comment.id)
editComment(comment.id);
}
setCommentsEditOpen((prevValue) => ({...prevValue, [comment.id]: !prevValue[comment.id]}))
}} />
setCommentsEditOpen((prevValue) => ({
...prevValue,
[comment.id]: !prevValue[comment.id],
}));
}}
/>
</div>
<img src={del} alt='delete' onClick={() => deleteComment(comment.id)} />
<img
src={del}
alt="delete"
onClick={() => deleteComment(comment.id)}
/>
</div>
{commentsEditOpen[comment.id] ? <input value={commentsEditText[comment.id]} onChange={(e) => {
setCommentsEditText((prevValue) => ({...prevValue, [comment.id]: e.target.value}))
}} /> : <p>{commentsEditText[comment.id]}</p>}
{commentsEditOpen[comment.id] ? (
<input
value={commentsEditText[comment.id]}
onChange={(e) => {
setCommentsEditText((prevValue) => ({
...prevValue,
[comment.id]: e.target.value,
}));
}}
/>
) : (
<p>{commentsEditText[comment.id]}</p>
)}
</div>
})
}
);
})}
</div>
</div>
</div>
<div className="workers">
<div className="workers_box task__info">
<span className="exit" onClick={() => setActive(false)}></span>
<span className='nameProject'>{task.title}</span>
<span className="nameProject">{task.title}</span>
<p className="workers__creator">Создатель : {task.user?.fio}</p>
{executor ?
<div className='executor'>
{executor ? (
<div className="executor">
<p>Исполнитель: {executor.fio}</p>
<img src={urlForLocal(executor.avatar)} alt='avatar' />
<img src={close} className='delete' onClick={() => deleteTaskExecutor()} />
</div> :
<img src={urlForLocal(executor.avatar)} alt="avatar" />
<img
src={close}
className="delete"
onClick={() => deleteTaskExecutor()}
/>
</div>
) : (
<div className="add-worker moreItems ">
<button onClick={() => setDropListOpen(true)}>+</button>
<span>Добавить исполнителя</span>
{dropListOpen &&
<div className='dropdownList'>
<img src={close} className='dropdownList__close' onClick={() => setDropListOpen(false)} />
{dropListOpen && (
<div className="dropdownList">
<img
src={close}
className="dropdownList__close"
onClick={() => setDropListOpen(false)}
/>
{projectUsers.map((person) => {
return <div className='dropdownList__person' key={person.user_id} onClick={() => taskExecutor(person)}>
return (
<div
className="dropdownList__person"
key={person.user_id}
onClick={() => taskExecutor(person)}
>
<span>{person.user.fio}</span>
<img src={urlForLocal(person.user.avatar)} />
</div>
})
}
);
})}
</div>
}
)}
</div>
}
)}
{Boolean(members.length) &&
<div className='members'>
{Boolean(members.length) && (
<div className="members">
<p>Участники:</p>
<div className='members__list'>
<div className="members__list">
{members.map((member) => {
return <div className='worker' key={member.user_id}>
return (
<div className="worker" key={member.user_id}>
<p>{member.fio}</p>
<img src={urlForLocal(member.avatar)} />
<img src={close} className='delete' onClick={() => deleteMember(member)} />
<img
src={close}
className="delete"
onClick={() => deleteMember(member)}
/>
</div>
})
}
);
})}
</div>
</div>
}
)}
<div className="add-worker moreItems">
<button onClick={() => setDropListMembersOpen(true)}>+</button>
<span>Добавить участников</span>
{dropListMembersOpen &&
<div className='dropdownList'>
<img src={close} className='dropdownList__close' onClick={() => setDropListMembersOpen(false)} />
{users.length ? users.map((person) => {
return <div className='dropdownList__person' key={person.user_id} onClick={() => addMember(person)}>
{dropListMembersOpen && (
<div className="dropdownList">
<img
src={close}
className="dropdownList__close"
onClick={() => setDropListMembersOpen(false)}
/>
{users.length ? (
users.map((person) => {
return (
<div
className="dropdownList__person"
key={person.user_id}
onClick={() => addMember(person)}
>
<span>{person.user.fio}</span>
<img src={urlForLocal(person.user.avatar)} />
</div>
}) : <p className='noUsers'>Нет пользователей</p>
}
);
})
) : (
<p className="noUsers">Нет пользователей</p>
)}
</div>
}
)}
</div>
</div>
@ -344,16 +446,19 @@ export const ModalTiсket = ({
</div>
<div className="workers_box-bottom">
<div className={editOpen ? 'edit' : ''} onClick={() => {
<div
className={editOpen ? "edit" : ""}
onClick={() => {
if (editOpen) {
setEditOpen(!editOpen)
editTask()
setEditOpen(!editOpen);
editTask();
} else {
setEditOpen(!editOpen)
setEditOpen(!editOpen);
}
}}>
}}
>
<img src={edit}></img>
<p>{editOpen ? 'сохранить' : 'редактировать'}</p>
<p>{editOpen ? "сохранить" : "редактировать"}</p>
</div>
<div>
<img src={link}></img>

View File

@ -15,14 +15,15 @@ import {
setProjectBoardFetch,
setToggleTab,
getProjectBoard,
getBoarderLoader
getBoarderLoader,
} from "../../../redux/projectsTrackerSlice";
import { apiRequest } from "../../../api/request";
import { urlForLocal } from "../../../helper";
import { getCorrectDate } from "../../Calendar/calendarHelper";
import project from "../../../images/trackerProject.svg";
import watch from "../../../images/watch.png";
import watch from "../../../images/watch.svg";
import file from "../../../images/fileModal.svg";
import task from "../../../images/tasksMock.png";
import send from "../../../images/send.svg";
import arrow2 from "../../../images/arrowStart.png";
import plus from "../../../images/plus.svg";
@ -34,11 +35,9 @@ import link from "../../../images/link.svg";
import archive2 from "../../../images/archive.svg";
import del from "../../../images/delete.svg";
import edit from "../../../images/edit.svg";
import close from "../../../images/closeProjectPersons.svg";
import "./ticketFullScreen.scss";
import close from "../../../images/closeProjectPersons.svg";
import {urlForLocal} from "../../../helper";
import {getCorrectDate} from "../../Calendar/calendarHelper";
export const TicketFullScreen = ({}) => {
const [modalAddWorker, setModalAddWorker] = useState(false);
@ -52,23 +51,35 @@ export const TicketFullScreen = ({}) => {
const [inputsValue, setInputsValue] = useState({});
const [loader, setLoader] = useState(true);
const [comments, setComments] = useState([]);
const [commentsEditOpen, setCommentsEditOpen] = useState({})
const [commentsEditText, setCommentsEditText] = useState({})
const [personListOpen, setPersonListOpen] = useState(false)
const [commentsEditOpen, setCommentsEditOpen] = useState({});
const [commentsEditText, setCommentsEditText] = useState({});
const [personListOpen, setPersonListOpen] = useState(false);
useEffect(() => {
apiRequest(`/task/get-task?task_id=${ticketId.id}`).then((taskInfo) => {
setTaskInfo(taskInfo);
setInputsValue({title: taskInfo.title, description: taskInfo.description, comment: ''})
apiRequest(`/comment/get-by-entity?entity_type=2&entity_id=${taskInfo.id}`).then((res) => {
setComments(res)
setInputsValue({
title: taskInfo.title,
description: taskInfo.description,
comment: "",
});
apiRequest(
`/comment/get-by-entity?entity_type=2&entity_id=${taskInfo.id}`
).then((res) => {
setComments(res);
res.forEach((item) => {
setCommentsEditOpen((prevValue) => ({...prevValue, [item.id]: false}))
setCommentsEditText((prevValue) => ({...prevValue, [item.id]: item.text}))
})
})
setCommentsEditOpen((prevValue) => ({
...prevValue,
[item.id]: false,
}));
setCommentsEditText((prevValue) => ({
...prevValue,
[item.id]: item.text,
}));
});
});
dispatch(setProjectBoardFetch(taskInfo.project_id));
setLoader(boardLoader)
setLoader(boardLoader);
});
}, []);
@ -90,10 +101,9 @@ export const TicketFullScreen = ({}) => {
data: {
task_id: taskInfo.id,
title: inputsValue.title,
description: inputsValue.description
description: inputsValue.description,
},
}).then((res) => {
});
}).then((res) => {});
}
function createComment() {
@ -102,16 +112,19 @@ export const TicketFullScreen = ({}) => {
data: {
text: inputsValue.comment,
entity_type: 2,
entity_id: taskInfo.id
}
entity_id: taskInfo.id,
},
}).then((res) => {
let newComment = res
newComment.created_at = new Date()
setInputsValue((prevValue) => ({...prevValue, comment: ''}))
setComments((prevValue) => ([...prevValue, newComment]))
setCommentsEditOpen((prevValue) => ({...prevValue, [res.id]: false}))
setCommentsEditText((prevValue) => ({...prevValue, [res.id]: res.text}))
})
let newComment = res;
newComment.created_at = new Date();
setInputsValue((prevValue) => ({ ...prevValue, comment: "" }));
setComments((prevValue) => [...prevValue, newComment]);
setCommentsEditOpen((prevValue) => ({ ...prevValue, [res.id]: false }));
setCommentsEditText((prevValue) => ({
...prevValue,
[res.id]: res.text,
}));
});
}
function deleteComment(commentId) {
@ -119,11 +132,13 @@ export const TicketFullScreen = ({}) => {
method: "PUT",
data: {
comment_id: commentId,
status: 0
}
status: 0,
},
}).then((res) => {
setComments((prevValue) => prevValue.filter((item) => item.id !== commentId))
})
setComments((prevValue) =>
prevValue.filter((item) => item.id !== commentId)
);
});
}
function editComment(commentId) {
@ -131,10 +146,9 @@ export const TicketFullScreen = ({}) => {
method: "PUT",
data: {
comment_id: commentId,
text: commentsEditText[commentId]
}
}).then((res) => {
})
text: commentsEditText[commentId],
},
}).then((res) => {});
}
function deletePerson(userId) {
@ -142,10 +156,10 @@ export const TicketFullScreen = ({}) => {
method: "DELETE",
data: {
project_id: projectBoard.id,
user_id: userId
user_id: userId,
},
}).then((res) => {
dispatch(deletePersonOnProject(userId))
dispatch(deletePersonOnProject(userId));
});
}
@ -195,7 +209,9 @@ export const TicketFullScreen = ({}) => {
<p>Архив</p>
</Link>
</div>
{loader ? <Loader /> :
{loader ? (
<Loader />
) : (
<>
<div className="tracker__tabs__content content-tabs">
<div className="tasks__head">
@ -210,43 +226,69 @@ export const TicketFullScreen = ({}) => {
<div className="tasks__head__persons">
{/*<img src={avatarTest} alt="avatar" />*/}
{/*<img src={avatarTest} alt="avatar" />*/}
<span className="countPersons">{projectBoard.projectUsers?.length}</span>
<span className="countPersons">
{projectBoard.projectUsers?.length}
</span>
<span
className="addPerson"
onClick={() => {
setPersonListOpen(true)
setPersonListOpen(true);
}}
>
+
</span>
<p>добавить участника</p>
{personListOpen &&
<div className='persons__list'>
<img className='persons__list__close' src={close} alt='close' onClick={() => setPersonListOpen(false)} />
<div className='persons__list__count'><span>{projectBoard.projectUsers?.length}</span>участник</div>
<div className='persons__list__info'>В проекте - <span>{projectBoard.name}</span></div>
<div className='persons__list__items'>
{personListOpen && (
<div className="persons__list">
<img
className="persons__list__close"
src={close}
alt="close"
onClick={() => setPersonListOpen(false)}
/>
<div className="persons__list__count">
<span>{projectBoard.projectUsers?.length}</span>
участник
</div>
<div className="persons__list__info">
В проекте - <span>{projectBoard.name}</span>
</div>
<div className="persons__list__items">
{projectBoard.projectUsers?.map((person) => {
return <div className='persons__list__item' key={person.user_id}>
<img className='avatar' src={urlForLocal(person.user.avatar)} alt='avatar' />
return (
<div
className="persons__list__item"
key={person.user_id}
>
<img
className="avatar"
src={urlForLocal(person.user.avatar)}
alt="avatar"
/>
<span>{person.user.fio}</span>
<img className='delete' src={close} alt='delete' onClick={() => deletePerson(person.user_id)}/>
<img
className="delete"
src={close}
alt="delete"
onClick={() => deletePerson(person.user_id)}
/>
</div>
})
}
);
})}
</div>
<div className='persons__list__add'
<div
className="persons__list__add"
onClick={() => {
dispatch(modalToggle("addWorker"));
setModalAddWorker(true);
setPersonListOpen(false)
setPersonListOpen(false);
}}
>
<span className='addPerson'>+</span>
<span className="addPerson">+</span>
<p>Добавить участников</p>
</div>
</div>
}
)}
</div>
<div className="tasks__head__select">
<span>Учавствую</span>
@ -269,13 +311,33 @@ export const TicketFullScreen = ({}) => {
<div className="content ticket-whith">
<div className="content__task">
<span>Задача</span>
{editOpen ? <input value={inputsValue.title} onChange={(e) => {
setInputsValue((prevValue) => ({...prevValue, title: e.target.value}))
}} /> :<h5>{inputsValue.title}</h5>}
{editOpen ? (
<input
value={inputsValue.title}
onChange={(e) => {
setInputsValue((prevValue) => ({
...prevValue,
title: e.target.value,
}));
}}
/>
) : (
<h5>{inputsValue.title}</h5>
)}
<div className="content__description">
{editOpen ? <input value={inputsValue.description} onChange={(e) => {
setInputsValue((prevValue) => ({...prevValue, description: e.target.value}))
}}/> :<p>{inputsValue.description}</p>}
{editOpen ? (
<input
value={inputsValue.description}
onChange={(e) => {
setInputsValue((prevValue) => ({
...prevValue,
description: e.target.value,
}));
}}
/>
) : (
<p>{inputsValue.description}</p>
)}
{/*<img src={task} className="image-task"></img>*/}
</div>
<div className="content__communication">
@ -300,33 +362,67 @@ export const TicketFullScreen = ({}) => {
</p>
</div>
<div className="content__input">
<input placeholder="Оставить комментарий" value={inputsValue.comment} onChange={(e) => {
setInputsValue((prevValue) => ({...prevValue, comment: e.target.value}))
}} />
<input
placeholder="Оставить комментарий"
value={inputsValue.comment}
onChange={(e) => {
setInputsValue((prevValue) => ({
...prevValue,
comment: e.target.value,
}));
}}
/>
<img src={send} onClick={createComment}></img>
</div>
<div className='comments__list'>
<div className="comments__list">
{comments.map((comment) => {
return <div className='comments__list__item' key={comment.id}>
<div className='comments__list__item__info'>
return (
<div className="comments__list__item" key={comment.id}>
<div className="comments__list__item__info">
<span>{getCorrectDate(comment.created_at)}</span>
<div className={commentsEditOpen[comment.id] ? 'edit edit__open' : 'edit'} >
<img src={edit} alt='edit' onClick={() => {
<div
className={
commentsEditOpen[comment.id]
? "edit edit__open"
: "edit"
}
>
<img
src={edit}
alt="edit"
onClick={() => {
if (commentsEditOpen[comment.id]) {
editComment(comment.id)
editComment(comment.id);
}
setCommentsEditOpen((prevValue) => ({...prevValue, [comment.id]: !prevValue[comment.id]}))
}} />
setCommentsEditOpen((prevValue) => ({
...prevValue,
[comment.id]: !prevValue[comment.id],
}));
}}
/>
</div>
<img src={del} alt='delete' onClick={() => deleteComment(comment.id)} />
<img
src={del}
alt="delete"
onClick={() => deleteComment(comment.id)}
/>
</div>
{commentsEditOpen[comment.id] ? <input value={commentsEditText[comment.id]} onChange={(e) => {
setCommentsEditText((prevValue) => ({...prevValue, [comment.id]: e.target.value}))
}} /> : <p>{commentsEditText[comment.id]}</p>}
{commentsEditOpen[comment.id] ? (
<input
value={commentsEditText[comment.id]}
onChange={(e) => {
setCommentsEditText((prevValue) => ({
...prevValue,
[comment.id]: e.target.value,
}));
}}
/>
) : (
<p>{commentsEditText[comment.id]}</p>
)}
</div>
})
}
);
})}
</div>
</div>
</div>
@ -384,16 +480,19 @@ export const TicketFullScreen = ({}) => {
</div>
<div className="workers_box-bottom">
<div className={editOpen ? 'edit' : ''} onClick={() => {
<div
className={editOpen ? "edit" : ""}
onClick={() => {
if (editOpen) {
setEditOpen(!editOpen)
editTask()
setEditOpen(!editOpen);
editTask();
} else {
setEditOpen(!editOpen)
setEditOpen(!editOpen);
}
}}>
}}
>
<img src={edit}></img>
<p>{editOpen ? 'сохранить' : 'редактировать'}</p>
<p>{editOpen ? "сохранить" : "редактировать"}</p>
</div>
<div>
<img src={link}></img>
@ -411,7 +510,7 @@ export const TicketFullScreen = ({}) => {
</div>
</div>
</>
}
)}
</div>
<Footer />
</section>

View File

@ -2,7 +2,7 @@ import React, {useEffect, useState} from "react";
import { useDispatch, useSelector } from "react-redux";
import { apiRequest } from "../../../api/request";
import { urlForLocal } from '../../../helper'
import { urlForLocal } from "../../../helper";
import {
setColumnName,
getProjectBoard,
@ -13,10 +13,10 @@ import {
editColumnName,
getColumnName,
getColumnId,
addPersonToProject
addPersonToProject,
} from "../../../redux/projectsTrackerSlice";
import arrowDown from "../../../images/selectArrow.png"
import arrowDown from "../../../images/selectArrow.png";
import "./trackerModal.scss";
@ -27,12 +27,12 @@ export const TrackerModal = ({
defautlInput,
titleProject,
projectId,
priorityTask
priorityTask,
}) => {
const dispatch = useDispatch();
const projectBoard = useSelector(getProjectBoard);
const columnName = useSelector(getColumnName);
const columnId = useSelector(getColumnId)
const columnId = useSelector(getColumnId);
const modalType = useSelector(getValueModalType);
const [projectName, setProjectName] = useState(defautlInput);
@ -40,9 +40,9 @@ export const TrackerModal = ({
const [nameProject, setNameProject] = useState("");
const [valueTiket, setValueTiket] = useState("");
const [descriptionTicket, setDescriptionTicket] = useState("");
const [workers, setWorkers] = useState([])
const [selectWorkersOpen, setSelectWorkersOpen] = useState(false)
const [selectedWorker, setSelectedWorker] = useState(null)
const [workers, setWorkers] = useState([]);
const [selectWorkersOpen, setSelectWorkersOpen] = useState(false);
const [selectedWorker, setSelectedWorker] = useState(null);
function createTab() {
if (!valueColumn) {
@ -76,7 +76,7 @@ export const TrackerModal = ({
status: 1,
user_id: localStorage.getItem("id"),
column_id: selectedTab,
priority: priorityTask
priority: priorityTask,
},
}).then((res) => {
dispatch(setProjectBoardFetch(projectBoard.id));
@ -105,12 +105,12 @@ export const TrackerModal = ({
method: "PUT",
data: {
column_id: columnId,
title: columnName
}
title: columnName,
},
}).then((res) => {
setActive(false);
dispatch(editColumnName({id: columnId, title: columnName}))
})
dispatch(editColumnName({ id: columnId, title: columnName }));
});
}
function createProject() {
@ -138,34 +138,37 @@ export const TrackerModal = ({
method: "POST",
data: {
user_id: selectedWorker.user_id,
project_id: projectBoard.id
}
project_id: projectBoard.id,
},
}).then((el) => {
dispatch(addPersonToProject(el))
dispatch(addPersonToProject(el));
setActive(false);
setSelectedWorker('')
setSelectWorkersOpen(false)
})
setSelectedWorker("");
setSelectWorkersOpen(false);
});
}
useEffect(() => {
modalType === "addWorker" ? apiRequest('/project/my-employee').then((el) => {
let persons = el.managerEmployees
let ids = projectBoard.projectUsers.map((user) => user.user_id)
setWorkers(persons.reduce((acc, cur) => {
if (!ids.includes(cur.user_id)) acc.push(cur)
return acc
}, []))
}) : ''
}, [active])
modalType === "addWorker"
? apiRequest("/project/my-employee").then((el) => {
let persons = el.managerEmployees;
let ids = projectBoard.projectUsers.map((user) => user.user_id);
setWorkers(
persons.reduce((acc, cur) => {
if (!ids.includes(cur.user_id)) acc.push(cur);
return acc;
}, [])
);
})
: "";
}, [active]);
return (
<div
className={active ? "modal-add active" : "modal-add"}
onClick={() => {
setActive(false)
setSelectWorkersOpen(false)
setActive(false);
setSelectWorkersOpen(false);
}}
>
<div className="modal-add__content" onClick={(e) => e.stopPropagation()}>
@ -180,35 +183,49 @@ export const TrackerModal = ({
{/* onChange={(e) => setEmailWorker(e.target.value)}*/}
{/* />*/}
{/*</div>*/}
<div className={selectWorkersOpen ? 'select__worker open' : 'select__worker'} onClick={() => setSelectWorkersOpen(!selectWorkersOpen)}>
<p>{selectedWorker ? selectedWorker.employee.fio : 'Выберите пользователя'}</p>
<img className='arrow' src={arrowDown} alt='arrow' />
{Boolean(selectWorkersOpen) &&
<div className='select__worker__dropDown'>
{Boolean(workers.length) ?
<div
className={
selectWorkersOpen ? "select__worker open" : "select__worker"
}
onClick={() => setSelectWorkersOpen(!selectWorkersOpen)}
>
<p>
{selectedWorker
? selectedWorker.employee.fio
: "Выберите пользователя"}
</p>
<img className="arrow" src={arrowDown} alt="arrow" />
{Boolean(selectWorkersOpen) && (
<div className="select__worker__dropDown">
{Boolean(workers.length) ? (
workers.map((worker) => {
if (worker === selectedWorker) {
return
return;
}
return <div className='worker' key={worker.id} onClick={() =>
{
setSelectedWorker(worker)
}
}>
<p>{worker.employee.fio}</p>
<img src={urlForLocal(worker.employee.avatar)} alt='avatar'/>
</div>
}) :
<div>Нет пользователей</div>
}
</div>
}
</div>
</div>
<button
className="button-add"
onClick={addUserToProject}
return (
<div
className="worker"
key={worker.id}
onClick={() => {
setSelectedWorker(worker);
}}
>
<p>{worker.employee.fio}</p>
<img
src={urlForLocal(worker.employee.avatar)}
alt="avatar"
/>
</div>
);
})
) : (
<div>Нет пользователей</div>
)}
</div>
)}
</div>
</div>
<button className="button-add" onClick={addUserToProject}>
Добавить
</button>
</div>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 B

6
src/images/category.svg Normal file
View File

@ -0,0 +1,6 @@
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.75626 7.30423H3.39734C1.75305 7.30423 0.849365 6.40055 0.849365 4.75626V3.39734C0.849365 1.75305 1.75305 0.849365 3.39734 0.849365H4.75626C6.40055 0.849365 7.30423 1.75305 7.30423 3.39734V4.75626C7.30423 6.40055 6.40055 7.30423 4.75626 7.30423ZM3.39734 1.86855C2.32379 1.86855 1.86855 2.32379 1.86855 3.39734V4.75626C1.86855 5.8298 2.32379 6.28504 3.39734 6.28504H4.75626C5.8298 6.28504 6.28504 5.8298 6.28504 4.75626V3.39734C6.28504 2.32379 5.8298 1.86855 4.75626 1.86855H3.39734Z" fill="#787486"/>
<path d="M12.9098 7.30423H11.5508C9.90655 7.30423 9.00287 6.40055 9.00287 4.75626V3.39734C9.00287 1.75305 9.90655 0.849365 11.5508 0.849365H12.9098C14.5541 0.849365 15.4577 1.75305 15.4577 3.39734V4.75626C15.4577 6.40055 14.5541 7.30423 12.9098 7.30423ZM11.5508 1.86855C10.4773 1.86855 10.0221 2.32379 10.0221 3.39734V4.75626C10.0221 5.8298 10.4773 6.28504 11.5508 6.28504H12.9098C13.9833 6.28504 14.4385 5.8298 14.4385 4.75626V3.39734C14.4385 2.32379 13.9833 1.86855 12.9098 1.86855H11.5508Z" fill="#787486"/>
<path d="M12.9098 15.4577H11.5508C9.90655 15.4577 9.00287 14.554 9.00287 12.9097V11.5508C9.00287 9.90649 9.90655 9.00281 11.5508 9.00281H12.9098C14.5541 9.00281 15.4577 9.90649 15.4577 11.5508V12.9097C15.4577 14.554 14.5541 15.4577 12.9098 15.4577ZM11.5508 10.022C10.4773 10.022 10.0221 10.4772 10.0221 11.5508V12.9097C10.0221 13.9832 10.4773 14.4385 11.5508 14.4385H12.9098C13.9833 14.4385 14.4385 13.9832 14.4385 12.9097V11.5508C14.4385 10.4772 13.9833 10.022 12.9098 10.022H11.5508Z" fill="#787486"/>
<path d="M4.75626 15.4577H3.39734C1.75305 15.4577 0.849365 14.554 0.849365 12.9097V11.5508C0.849365 9.90649 1.75305 9.00281 3.39734 9.00281H4.75626C6.40055 9.00281 7.30423 9.90649 7.30423 11.5508V12.9097C7.30423 14.554 6.40055 15.4577 4.75626 15.4577ZM3.39734 10.022C2.32379 10.022 1.86855 10.4772 1.86855 11.5508V12.9097C1.86855 13.9832 2.32379 14.4385 3.39734 14.4385H4.75626C5.8298 14.4385 6.28504 13.9832 6.28504 12.9097V11.5508C6.28504 10.4772 5.8298 10.022 4.75626 10.022H3.39734Z" fill="#787486"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 268 B

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

8
src/images/remove.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 475 B

12
src/images/watch.svg Normal file
View File

@ -0,0 +1,12 @@
<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_323_3463)">
<path d="M14 7.00317C13.9868 10.8653 10.8389 14.0106 6.99629 14.0011C3.1283 13.9921 -0.0285369 10.8199 -6.48721e-06 6.97094C0.0290523 3.11563 3.16106 -0.00264003 7.00211 1.67724e-06C10.8532 0.00264339 14.0127 3.1653 13.9995 7.00317H14ZM0.802544 6.98996C0.786694 10.3957 3.55151 13.1768 6.97569 13.1996C10.3809 13.2223 13.1679 10.4617 13.1969 7.03699C13.2254 3.61914 10.4612 0.827385 7.02324 0.801496C3.61068 0.776136 0.818923 3.55363 0.803073 6.98943L0.802544 6.98996Z" fill="#DDE2E4"/>
<path d="M7.40101 4.01752C7.40101 4.51891 7.40471 5.02031 7.39837 5.5217C7.39678 5.66647 7.42003 5.75787 7.57748 5.82075C7.84693 5.92853 8.05087 6.13247 8.16129 6.40087C8.23156 6.57099 8.33195 6.60692 8.50049 6.60375C9.06317 6.59371 9.62639 6.60005 10.1896 6.60005C10.3227 6.60005 10.4569 6.6048 10.561 6.70202C10.6878 6.82037 10.7306 6.96619 10.6714 7.13315C10.6033 7.32599 10.4522 7.39943 10.2609 7.40049C9.6713 7.40313 9.08219 7.4063 8.49257 7.39943C8.33723 7.39785 8.24583 7.42532 8.17186 7.58858C7.93781 8.10371 7.38146 8.39219 6.83304 8.31558C6.24606 8.23315 5.7901 7.78829 5.69024 7.19972C5.59937 6.66345 5.87992 6.0865 6.38924 5.84294C6.56782 5.75734 6.60269 5.65749 6.60163 5.48261C6.59476 4.53265 6.59793 3.58216 6.59846 2.6322C6.59846 2.55295 6.59529 2.4737 6.60163 2.39498C6.62171 2.14137 6.76964 1.98815 6.99313 1.98446C7.22613 1.98023 7.39097 2.14085 7.39784 2.40819C7.40788 2.79493 7.40101 3.18221 7.40154 3.56948C7.40154 3.719 7.40154 3.86852 7.40154 4.01804L7.40101 4.01752ZM7.52623 6.99314C7.52147 6.70783 7.26153 6.46162 6.97887 6.4743C6.70254 6.48646 6.46796 6.73425 6.47271 7.00793C6.47747 7.28372 6.71839 7.52306 6.99419 7.52623C7.27896 7.5294 7.53151 7.27633 7.52676 6.99261L7.52623 6.99314Z" fill="#DDE2E4"/>
<path d="M7.52623 6.99314C7.53099 7.27686 7.27844 7.52993 6.99367 7.52676C6.71787 7.52359 6.47695 7.28373 6.47219 7.00846C6.46744 6.73425 6.70255 6.48699 6.97834 6.47483C7.26154 6.46215 7.52095 6.70836 7.52571 6.99367L7.52623 6.99314Z" fill="#FBFBFB"/>
</g>
<defs>
<clipPath id="clip0_323_3463">
<rect width="14" height="14.0016" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -8,7 +8,7 @@ import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileB
import CardArticle from "../../components/UI/CardArticle/CardArticle";
import mockImgArticle from "../../images/mockImgArticle.png";
import rightArrow from "../../images/right-arrow.png";
import rightArrow from "../../images/left-arrow.png";
import yandexZen from "../../images/yandexZen.svg";
import cardCalendar from "../../images/cardCalendar.svg";
import cardImg1 from "../../images/cardArticleItem.png";

View File

@ -1,29 +1,31 @@
import React, {useEffect} from 'react'
import {useDispatch, useSelector} from 'react-redux'
import {useParams, useNavigate, Navigate} from 'react-router-dom'
import SVG from 'react-inlinesvg'
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useParams, useNavigate, Navigate } from "react-router-dom";
import SVG from "react-inlinesvg";
import Form from '../../components/Form/Form'
import {Footer} from '../../components/Footer/Footer'
import Form from "../../components/Form/Form";
import { Footer } from "../../components/Footer/Footer";
import { LogoutButton } from "../../components/LogoutButton/LogoutButton";
import arrow from "../../images/left-arrow.png";
import rectangle from "../../images/rectangle_secondPage.png";
import telegramIcon from "../../images/telegram-icon.svg";
import arrow from '../../images/right-arrow.png'
import rectangle from '../../images/rectangle_secondPage.png'
import telegramIcon from '../../images/telegram-icon.svg'
import { LEVELS, SKILLS } from "../../constants/constants";
import {LEVELS, SKILLS} from '../../constants/constants'
import {currentCandidate, selectCurrentCandidate} from '../../redux/outstaffingSlice'
import {
currentCandidate,
selectCurrentCandidate,
} from "../../redux/outstaffingSlice";
import { apiRequest } from "../../api/request";
import { urlForLocal } from "../../helper";
import './formPage.scss'
import "./formPage.scss";
const FormPage = () => {
if(localStorage.getItem('role_status') !== '18') {
return <Navigate to="/profile" replace/>
if (localStorage.getItem("role_status") !== "18") {
return <Navigate to="/profile" replace />;
}
const params = useParams();
const navigate = useNavigate();
@ -31,64 +33,64 @@ const FormPage = () => {
const candidate = useSelector(selectCurrentCandidate);
const goBack = () => {
navigate(-1)
navigate(-1);
};
useEffect(() => {
if (!candidate.id) {
apiRequest('/profile', {
params: Number(params.id)
})
.then((el) => dispatch(currentCandidate(el)))
apiRequest("/profile", {
params: Number(params.id),
}).then((el) => dispatch(currentCandidate(el)));
}
}, []);
return (
<div className='container'>
<div className='form-page'>
<div className='form-page__back'>
<div className='form-page__arrow' onClick={goBack}>
<div className='form-page__arrow-img'>
<img src={arrow} alt=''/>
<div className="container">
<div className="form-page">
<div className="form-page__back">
<div className="form-page__arrow" onClick={goBack}>
<div className="form-page__arrow-img">
<img src={arrow} alt="" />
</div>
<div className='form-page__back-to-candidate'>
<div className="form-page__back-to-candidate">
<span>Вернуться к кандидату</span>
</div>
</div>
<LogoutButton />
</div>
<div className='form-page__candidate'>
<div className='form-page__avatar'>
{candidate.photo && <img src={urlForLocal(candidate.photo)} alt='candidate avatar'/>}
<div className="form-page__candidate">
<div className="form-page__avatar">
{candidate.photo && (
<img src={urlForLocal(candidate.photo)} alt="candidate avatar" />
)}
</div>
<div className='form-page__candidate-info'>
<div className='form-page__position'>
<div className="form-page__candidate-info">
<div className="form-page__position">
<span>
{candidate.specification} {SKILLS[candidate.position_id]},{' '}
{candidate.specification} {SKILLS[candidate.position_id]},{" "}
{LEVELS[candidate.level]}
</span>
</div>
<div className='form-page__selected'>
<img src={rectangle} alt='rectangle'/>
<div className="form-page__selected">
<img src={rectangle} alt="rectangle" />
<span>Выбранный кандидат</span>
</div>
</div>
</div>
<div className='form-page__interview'>
<div className='form-page__form'>
<div className="form-page__interview">
<div className="form-page__form">
<Form />
</div>
<div className='form-page__separator'>
<div className='form-page__line'></div>
<div className='form-page__option'>или</div>
<div className="form-page__separator">
<div className="form-page__line"></div>
<div className="form-page__option">или</div>
</div>
<div className='form-page__telegram'>
<div className='form-page__telegram-text'>
<div className="form-page__telegram">
<div className="form-page__telegram-text">
Заявка на собеседование через телеграм
</div>
<div className='form-page__telegram-icon'>
<a href='https://t.me/st0kir' target='_blank' rel="noreferrer">
<div className="form-page__telegram-icon">
<a href="https://t.me/st0kir" target="_blank" rel="noreferrer">
<SVG src={telegramIcon} />
</a>
</div>
@ -97,7 +99,7 @@ const FormPage = () => {
<Footer />
</div>
</div>
)
);
};
export default FormPage
export default FormPage;

View File

@ -1,59 +1,59 @@
import React from 'react'
import arrowLeft from '../../images/right-arrow.png'
import SVG from 'react-inlinesvg'
import dateArrowIcon from '../../images/dateArrow.svg'
import calendarIcon from '../../images/calendar.svg'
import { TaskItem } from '../../components/TaskItem/TaskItem'
import './singleReportPage.scss'
import React from "react";
import { useNavigate } from "react-router";
import SVG from "react-inlinesvg";
import { TaskItem } from "../../components/TaskItem/TaskItem";
import { LogoutButton } from "../../components/LogoutButton/LogoutButton";
import arrowLeft from "../../images/left-arrow.png";
import dateArrowIcon from "../../images/dateArrow.svg";
import calendarIcon from "../../images/calendar.svg";
import "./singleReportPage.scss";
const tasks = [
{
index: 1,
text: 'Задача «67 Навигационная система Главное меню Обновить иконки» заблокирована из-за отсутствия новых иконок',
hours: 3
text: "Задача «67 Навигационная система Главное меню Обновить иконки» заблокирована из-за отсутствия новых иконок",
hours: 3,
},
{
index: 2,
text: 'Задача «83 Навигационная система Поиск по почтовому индексу Добавить экран поиска по почтовому индексу» не может быть завершена, т.к. работа над задачей «82 Навигационная система Разработать модуль поиска по почтовому индексу» ещё не начата',
hours: 3
}
text: "Задача «83 Навигационная система Поиск по почтовому индексу Добавить экран поиска по почтовому индексу» не может быть завершена, т.к. работа над задачей «82 Навигационная система Разработать модуль поиска по почтовому индексу» ещё не начата",
hours: 3,
},
];
const SingleReportPage = () => {
const navigate = useNavigate();
return (
<div className='single-report-page'>
<div onClick={()=> {navigate(-1)}} className='single-report-page__back'>
<div className='single-report-page__back-arrow'>
<img src={arrowLeft} alt='arrowLeft'/>
</div>
<div className='single-report-page__back-text'>
Вернуться к списку
<div className="single-report-page">
<div
onClick={() => {
navigate(-1);
}}
className="single-report-page__back"
>
<div className="single-report-page__back-arrow">
<img src={arrowLeft} alt="arrowLeft" />
</div>
<div className="single-report-page__back-text">Вернуться к списку</div>
</div>
<div className='single-report-page__title'>
<div className='single-report-page__title-text'>Отчет за день</div>
<div className='single-report-page__title-date'>
<div className='single-report-page__title-date--prev'>
<div className="single-report-page__title">
<div className="single-report-page__title-text">Отчет за день</div>
<div className="single-report-page__title-date">
<div className="single-report-page__title-date--prev">
<button>
<SVG src={dateArrowIcon} />
</button>
</div>
<div className='single-report-page__title-date--actual'>
<div className="single-report-page__title-date--actual">
<SVG src={calendarIcon} />
<p>15 июня</p>
</div>
<div className='single-report-page__title-date--next single-report-page__title-date--enabled'>
<div className="single-report-page__title-date--next single-report-page__title-date--enabled">
<button>
<SVG src={dateArrowIcon} />
</button>
@ -61,53 +61,52 @@ const SingleReportPage = () => {
</div>
</div>
<div className='single-report-page__tasks'>
<div className='single-report-page__tasks-title'>
<div className='single-report-page__marker'></div>
<div className="single-report-page__tasks">
<div className="single-report-page__tasks-title">
<div className="single-report-page__marker"></div>
<h3>Какие задачи были выполнены?</h3>
</div>
{tasks.map((task) => {
return (
<div className='single-report-page__tasks-item'>
<div className="single-report-page__tasks-item">
<TaskItem {...task} />
</div>
)
);
})}
</div>
<div className='single-report-page__troubles'>
<div className='single-report-page__troubles-title'>
<div className='single-report-page__marker'></div>
<div className="single-report-page__troubles">
<div className="single-report-page__troubles-title">
<div className="single-report-page__marker"></div>
<h3>Какие сложности возникли?</h3>
</div>
<div className='single-report-page__troubles-item'>
<div className="single-report-page__troubles-item">
91 Навигационная система Поиск адреса Разобраться, почему
находятся несколько пересечений Невского пр. и Казанской ул.
</div>
</div>
<div className='single-report-page__scheduled'>
<div className='single-report-page__scheduled-title'>
<div className='single-report-page__marker'></div>
<div className="single-report-page__scheduled">
<div className="single-report-page__scheduled-title">
<div className="single-report-page__marker"></div>
<h3>Что планируется сделать завтра?</h3>
</div>
<div className='single-report-page__scheduled-item'>
<div className="single-report-page__scheduled-item">
91 Навигационная система Поиск адреса Разобраться, почему
находятся несколько пересечений Невского пр. и Казанской ул.
</div>
</div>
<div className='single-report-page__footer'>
<div className='single-report-page__footer-rectangle'></div>
<div className='single-report-page__hours'>
<div className='single-report-page__hours-value'></div>
<div className='single-report-page__hours-text'></div>
<div className="single-report-page__footer">
<div className="single-report-page__footer-rectangle"></div>
<div className="single-report-page__hours">
<div className="single-report-page__hours-value"></div>
<div className="single-report-page__hours-text"></div>
</div>
</div>
<LogoutButton />
</div>
)
);
};
export default SingleReportPage
export default SingleReportPage;

View File

@ -10,7 +10,7 @@ import { urlForLocal } from "../../helper";
import { apiRequest } from "../../api/request";
import { Navigation } from "../../components/Navigation/Navigation";
import arrow from "../../images/right-arrow.png";
import arrow from "../../images/left-arrow.png";
import rightArrow from "../../images/arrowRight.svg";
import gitImgItem from "../../images/gitItemImg.svg";

View File

@ -14,7 +14,7 @@ import {
hourOfNum,
} from "../../components/Calendar/calendarHelper";
import arrow from "../../images/right-arrow.png";
import arrow from "../../images/left-arrow.png";
import arrowSwitchDate from "../../images/arrowViewReport.png";
import "./viewReport.scss";
@ -103,7 +103,7 @@ export const ViewReport = () => {
Ваши отчеты - <span>просмотр отчета за день</span>
</h2>
<Link className="viewReport__back" to={`/profile/calendar`}>
<img src={arrow} alt="arrow" />
<img src={arrow} alt="#" />
<p>Вернуться</p>
</Link>
<div className="viewReport__bar">