Merge branch 'main' into quiz
This commit is contained in:
@ -13,10 +13,10 @@ export const AuthBlock = ({ title, description, img, resetModal }) => {
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
<form className="auth__form">
|
||||
<label htmlFor="login">Ваш email *</label>
|
||||
<input id="login" type="text" name="username" placeholder="Email" />
|
||||
<label htmlFor="login">Ваш e-mail</label>
|
||||
<input id="login" type="text" name="username" placeholder="E-mail" />
|
||||
|
||||
<label htmlFor="password">Ваш пароль*</label>
|
||||
<label htmlFor="password">Ваш пароль</label>
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
@ -24,14 +24,8 @@ export const AuthBlock = ({ title, description, img, resetModal }) => {
|
||||
placeholder="Пароль"
|
||||
/>
|
||||
<div className="auth__form__buttons">
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
Войти
|
||||
</button>
|
||||
<span onClick={() => resetModal(true)}>Вспомнить пароль</span>
|
||||
<button onClick={(e) => e.preventDefault()}>Войти</button>
|
||||
<span onClick={() => resetModal(true)}>Восстановить пароль</span>
|
||||
</div>
|
||||
</form>
|
||||
{img && <img src={img} alt="authImg" className="auth__img" />}
|
||||
|
@ -5,27 +5,33 @@
|
||||
padding: 50px 0 35px 56px;
|
||||
margin-top: 40px;
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
flex-direction: column;
|
||||
padding: 25px;
|
||||
margin-top: 0;
|
||||
padding: 0;
|
||||
@media (max-width: 1400px) {
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
flex-direction: column-reverse;
|
||||
padding: 20px;
|
||||
margin-top: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
margin-right: 115px;
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
@media (max-width: 900px) {
|
||||
order: 3;
|
||||
flex-direction: column;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-weight: 500;
|
||||
font-size: 30px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
@ -35,7 +41,7 @@
|
||||
font-size: 16px;
|
||||
line-height: 30px;
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
@media (max-width: 900px) {
|
||||
max-width: none;
|
||||
margin: 15px 25px;
|
||||
}
|
||||
@ -46,7 +52,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
@media (max-width: 900px) {
|
||||
margin-right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -70,7 +76,7 @@
|
||||
font-size: 15px;
|
||||
line-height: 18px;
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
@media (max-width: 900px) {
|
||||
max-width: 550px;
|
||||
}
|
||||
}
|
||||
@ -109,9 +115,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
@media (max-width: 900px) {
|
||||
order: 2;
|
||||
margin-bottom: 55px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,7 +125,7 @@
|
||||
right: 48px;
|
||||
top: -90px;
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
@media (max-width: 900px) {
|
||||
order: 1;
|
||||
position: inherit;
|
||||
right: inherit;
|
||||
|
@ -11,6 +11,7 @@ import { apiRequest } from "@api/request";
|
||||
import { Loader } from "@components/Common/Loader/Loader";
|
||||
import ModalErrorLogin from "@components/Modal/ModalErrorLogin/ModalErrorLogin";
|
||||
import ModalRegistration from "@components/Modal/ModalRegistration/ModalRegistration";
|
||||
import ModalResetPassword from "@components/Modal/ModalResetPassword/ModalResetPassword";
|
||||
|
||||
import authHead from "assets/icons/authHead.svg";
|
||||
import eyePassword from "assets/icons/passwordIcon.svg";
|
||||
@ -27,6 +28,7 @@ export const AuthBox = ({ title }) => {
|
||||
|
||||
const [error, setError] = useState(null);
|
||||
const [modalError, setModalError] = useState(false);
|
||||
const [modalReset, setModalReset] = useState(false);
|
||||
const [modalReg, setModalReg] = useState(false);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
@ -51,7 +53,7 @@ export const AuthBox = ({ title }) => {
|
||||
data: formData,
|
||||
}).then((res) => {
|
||||
if (!res.access_token) {
|
||||
setError("Введены некоректные данные для входа");
|
||||
setError("Введены некорректные данные для входа");
|
||||
setModalError(true);
|
||||
dispatch(loading(false));
|
||||
} else {
|
||||
@ -77,14 +79,16 @@ export const AuthBox = ({ title }) => {
|
||||
<h2 className="auth-box__header">
|
||||
Вход <img src={authHead} alt="authImg" />
|
||||
</h2>
|
||||
<div className="auth-box__title">
|
||||
<span>{title}</span>
|
||||
</div>
|
||||
{title && (
|
||||
<div className="auth-box__title">
|
||||
<span>{title}</span>
|
||||
</div>
|
||||
)}
|
||||
<form ref={ref} className="auth-box__form">
|
||||
<label htmlFor="login">Ваш email *</label>
|
||||
<label htmlFor="login">Ваш e-mail</label>
|
||||
<input id="login" type="text" name="username" placeholder="Логин" />
|
||||
|
||||
<label htmlFor="password">Ваш пароль*</label>
|
||||
<label htmlFor="password">Ваш пароль</label>
|
||||
<div className="inputWrapper">
|
||||
<input
|
||||
id="password"
|
||||
@ -120,12 +124,14 @@ export const AuthBox = ({ title }) => {
|
||||
>
|
||||
{isLoading ? <Loader /> : "Войти"}
|
||||
</button>
|
||||
<span className="auth-box__reset">Вспомнить пароль</span>
|
||||
|
||||
<span className="auth-box__reset" onClick={() => setModalReset(true)}>
|
||||
Восстановить пароль
|
||||
</span>
|
||||
<ModalResetPassword active={modalReset} setActive={setModalReset} />
|
||||
<ModalRegistration active={modalReg} setActive={setModalReg} />
|
||||
</div>
|
||||
<p className="auth-box__registration">
|
||||
У вас еще нет аккаунта?
|
||||
У вас ещё нет аккаунта?
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
|
@ -15,7 +15,8 @@
|
||||
letter-spacing: normal;
|
||||
line-height: 77.81px;
|
||||
text-align: left;
|
||||
margin-top: 164px;
|
||||
margin-top: 150px;
|
||||
margin-bottom: 30px;
|
||||
|
||||
span {
|
||||
color: #52b709;
|
||||
@ -55,7 +56,7 @@
|
||||
margin-bottom: 54px;
|
||||
|
||||
span {
|
||||
color: #8DC63F;
|
||||
color: #8dc63f;
|
||||
font-size: 50px;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
@ -88,12 +89,12 @@
|
||||
letter-spacing: normal;
|
||||
line-height: 19.2px;
|
||||
text-align: left;
|
||||
margin-bottom: 25px;
|
||||
margin-bottom: 10px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.inputWrapper {
|
||||
width: 366px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.eye {
|
||||
@ -101,6 +102,10 @@
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 15px;
|
||||
|
||||
@media (min-width: 576px) {
|
||||
left: 330px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,7 +157,6 @@
|
||||
letter-spacing: normal;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
border: 2px solid #6aaf5c;
|
||||
margin-right: 1.5rem;
|
||||
transition: 0.3s;
|
||||
|
||||
@ -193,7 +197,7 @@
|
||||
|
||||
&__reset {
|
||||
color: #000000;
|
||||
font-size: 13px;
|
||||
font-size: 15px;
|
||||
line-height: 16px;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
@ -203,12 +207,16 @@
|
||||
color: #000000;
|
||||
font-size: 17px;
|
||||
font-weight: 400;
|
||||
margin-top: 45px;
|
||||
margin-top: 35px;
|
||||
line-height: 32px;
|
||||
|
||||
@media (max-width: 700px) {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
span {
|
||||
cursor: pointer;
|
||||
color: #52B709;
|
||||
color: #52b709;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
@ -61,9 +61,7 @@ const Calendar = () => {
|
||||
</div>
|
||||
<div>
|
||||
<Link to="/report">
|
||||
<button className="calendar__btn">
|
||||
Заполнить отчет за день
|
||||
</button>
|
||||
<button className="calendar__btn">Заполнить отчет</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -125,6 +125,10 @@
|
||||
margin: 50px 0 0;
|
||||
text-transform: capitalize;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
font-size: 1.7em;
|
||||
}
|
||||
|
||||
span {
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
|
@ -13,7 +13,7 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@media (max-width: 560px) {
|
||||
@media (max-width: 760px) {
|
||||
flex-direction: column;
|
||||
row-gap: 15px;
|
||||
}
|
||||
@ -21,20 +21,13 @@
|
||||
&-info {
|
||||
display: flex;
|
||||
|
||||
@media (max-width: 685px) {
|
||||
font-size: 7px;
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
@media (max-width: 760px) {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.calendar__hours {
|
||||
margin: 0 10px;
|
||||
margin: 0 5px;
|
||||
line-height: 0;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
@ -45,18 +38,14 @@
|
||||
&-switcher {
|
||||
display: flex;
|
||||
|
||||
@media (max-width: 590px) {
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
@media (max-width: 760px) {
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 2.5em;
|
||||
font-size: 2.2em;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
letter-spacing: normal;
|
||||
@ -165,6 +154,11 @@
|
||||
justify-content: center;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
font-size: 10px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
@ -182,27 +176,25 @@
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
width: 90px;
|
||||
height: 40px;
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
@media (max-width: 968px) {
|
||||
width: 62px;
|
||||
height: 40px;
|
||||
width: 90px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
@media (max-width: 680px) {
|
||||
width: 70px;
|
||||
font-size: 10px;
|
||||
|
||||
@media (max-width: 610px) {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 610px) {
|
||||
width: 55px;
|
||||
height: 45px;
|
||||
@media (max-width: 550px) {
|
||||
width: 60px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
@media (max-width: 450px) {
|
||||
width: 45px;
|
||||
height: 35px;
|
||||
}
|
||||
@ -294,12 +286,11 @@
|
||||
|
||||
.select {
|
||||
border-radius: 5px;
|
||||
border: 1px solid #c4c4c4;
|
||||
border: 2px solid #c4c4c4;
|
||||
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
|
||||
padding: 5px 8px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
min-width: 350px;
|
||||
}
|
||||
|
||||
.close {
|
||||
|
@ -32,7 +32,7 @@ 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,10 +47,10 @@ const Candidate = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
apiRequest(`/profile/${candidateId}`, {
|
||||
params: Number(candidateId),
|
||||
}).then((el) => dispatch(currentCandidate(el)));
|
||||
}, [dispatch, candidateId]);
|
||||
apiRequest(`/user/me`, {}).then((el) =>
|
||||
dispatch(currentCandidate(el.userCard))
|
||||
);
|
||||
}, [dispatch]);
|
||||
|
||||
const { position_id, skillValues, vc_text: text } = currentCandidateObj;
|
||||
|
||||
@ -174,7 +174,7 @@ const Candidate = () => {
|
||||
<div className="candidate__works works">
|
||||
<div className="works__body">
|
||||
<div className="works__body__info">
|
||||
<p>Страница портфолио кода разработчика</p>
|
||||
<p>Портфолио разработчика</p>
|
||||
</div>
|
||||
<div className="works__item item-works">
|
||||
<Link to="/" className="item-works__body">
|
||||
|
@ -12,7 +12,7 @@ export const AuthHeader = () => {
|
||||
<div className="auth-header">
|
||||
<div className="auth-header__logo">
|
||||
<h3>
|
||||
itguild.<span>аутстафинг ИТ специалистов</span>
|
||||
itguild.<span>аутстаффинг IT-специалистов</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div className="auth-header__navigation">
|
||||
@ -25,24 +25,10 @@ export const AuthHeader = () => {
|
||||
</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
scrollToForm();
|
||||
}}
|
||||
>
|
||||
Кабинет разработчика
|
||||
</a>
|
||||
<NavLink to={"/auth"}>Кабинет разработчика</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
scrollToForm();
|
||||
}}
|
||||
>
|
||||
Трекер
|
||||
</a>
|
||||
<NavLink to={"/tracker-intro"}>Трекер</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink to={"/auth-candidate"} className="candidate">
|
||||
|
@ -2,9 +2,13 @@ import React from "react";
|
||||
|
||||
import "./basebutton.scss";
|
||||
|
||||
export const BaseButton = ({ children, styles, ...props }) => {
|
||||
export const BaseButton = ({ children, styles, onClick, ...props }) => {
|
||||
return (
|
||||
<button className={styles ? `${styles} button` : "button"} {...props}>
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={styles ? `${styles} button` : "button"}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
|
@ -3,7 +3,7 @@ import React from "react";
|
||||
import email from "assets/icons/emailLogo.svg";
|
||||
import tg from "assets/icons/tgFooter.svg";
|
||||
import vk from "assets/icons/vkLogo.svg";
|
||||
import logo from "assets/images/logo/LogoITguild.svg";
|
||||
import logo from "assets/images/logo/ITguild.svg";
|
||||
|
||||
import "./footer.scss";
|
||||
|
||||
@ -27,21 +27,19 @@ export const Footer = () => {
|
||||
<div className="footer__bottom">
|
||||
<div className="footer__social">
|
||||
<div className="footer__social__icons">
|
||||
<a>
|
||||
<img src={vk} alt="vk" />
|
||||
<a href="https://www.vk.com/">
|
||||
<img src={vk} alt="vk" width={24} />
|
||||
</a>
|
||||
<a>
|
||||
<img src={tg} alt="tg" />
|
||||
<a href="https://www.telegram.org/">
|
||||
<img src={tg} alt="tg" width={24} />
|
||||
</a>
|
||||
</div>
|
||||
<p>Войти в команду</p>
|
||||
</div>
|
||||
<div className="footer__info">
|
||||
<div className="footer__mail">
|
||||
<a>
|
||||
<img src={email} alt="email" />
|
||||
</a>
|
||||
<p>office@itguild.info</p>
|
||||
<img src={email} alt="email" />
|
||||
<a href="mailto:office@itguild.info">office@itguild.info</a>
|
||||
</div>
|
||||
<a className="footer__policy">Политика конфиденциальности</a>
|
||||
</div>
|
||||
|
@ -79,11 +79,11 @@ footer {
|
||||
&__mail {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 13px;
|
||||
column-gap: 5px;
|
||||
|
||||
p {
|
||||
a {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
color: #5b6871;
|
||||
}
|
||||
@ -91,7 +91,7 @@ footer {
|
||||
|
||||
&__policy {
|
||||
font-weight: 400;
|
||||
font-size: 10px;
|
||||
font-size: 13px;
|
||||
line-height: 16px;
|
||||
color: #5b6871;
|
||||
margin-left: 150px;
|
||||
@ -108,6 +108,7 @@ footer {
|
||||
|
||||
&__copyright {
|
||||
margin-left: auto;
|
||||
font-size: 13px;
|
||||
|
||||
@media (max-width: 910px) {
|
||||
min-width: 142px;
|
||||
|
@ -13,7 +13,11 @@ export const ModalLayout = ({
|
||||
return (
|
||||
<div
|
||||
className={active ? `modal-layout active` : "modal-layout"}
|
||||
onClick={() => setActive(false)}
|
||||
onClick={(event) => {
|
||||
if (event.target.className === "modal-layout active") {
|
||||
setActive(false);
|
||||
}
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
@ -22,7 +26,6 @@ export const ModalLayout = ({
|
||||
? `modal-layout__content ${styles}`
|
||||
: `modal-layout__content ${type}`
|
||||
}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
@ -45,6 +45,16 @@
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.addPersonBlock {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
button {
|
||||
// margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.invitePersonBlock {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -64,7 +74,7 @@
|
||||
}
|
||||
|
||||
&__btn {
|
||||
margin: 0 auto 0 0;
|
||||
margin: 0;
|
||||
max-width: 242px;
|
||||
width: 100%;
|
||||
}
|
||||
@ -96,7 +106,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 10px;
|
||||
padding: 39px 10px 29px 10px;
|
||||
padding: 19px 10px 29px 10px;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
padding: 10px;
|
||||
|
@ -98,7 +98,7 @@ const Description = ({ onLoadMore }) => {
|
||||
<div className="description__footer">
|
||||
<div className="description__footer-btn">
|
||||
{candidatesListArr && (
|
||||
<button onClick={() => onLoadMore(2)}>Загрузить еще</button>
|
||||
<button onClick={() => onLoadMore(2)}>Загрузить ещё</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -85,13 +85,13 @@ const Form = () => {
|
||||
<div className="row">
|
||||
<div className="col-sm-12">
|
||||
<form className="form" id="test">
|
||||
<label htmlFor="email">Емейл:</label>
|
||||
<label htmlFor="email">E-mail:</label>
|
||||
<input
|
||||
onChange={handleChange}
|
||||
id="email"
|
||||
name="Email"
|
||||
type="email"
|
||||
placeholder="Емейл"
|
||||
placeholder="E-mail"
|
||||
value={data.email}
|
||||
/>
|
||||
|
||||
|
@ -50,7 +50,7 @@ export const FreeDevelopers = () => {
|
||||
|
||||
<div className="free-dev__body">
|
||||
<div className="free-dev__body-title">
|
||||
<p>Описание опыта работы</p>
|
||||
<p>Опыт работы</p>
|
||||
</div>
|
||||
|
||||
<div className="free-dev__body-text">
|
||||
@ -86,7 +86,7 @@ export const FreeDevelopers = () => {
|
||||
</div>
|
||||
|
||||
<div className="login">
|
||||
<h3>Для просмотра полного резюме разработчика авторизуйтесь</h3>
|
||||
<h3>Для просмотра полного резюме разработчика, авторизуйтесь</h3>
|
||||
<BaseButton styles={"dev-code"}>
|
||||
<Link to={"/auth"}>Войти</Link>
|
||||
</BaseButton>
|
||||
|
@ -1,9 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { getRole } from "@redux/roleSlice";
|
||||
|
||||
import { useLogout } from "@hooks/useLogout";
|
||||
|
||||
import { Loader } from "@components/Common/Loader/Loader";
|
||||
@ -12,8 +9,6 @@ import "./logoutButton.scss";
|
||||
|
||||
export const LogoutButton = () => {
|
||||
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
||||
|
||||
const userRole = useSelector(getRole);
|
||||
const navigate = useNavigate();
|
||||
const { logout } = useLogout();
|
||||
|
||||
@ -24,7 +19,7 @@ export const LogoutButton = () => {
|
||||
setIsLoggingOut(true);
|
||||
logout();
|
||||
setIsLoggingOut(false);
|
||||
navigate(userRole === "ROLE_DEV" ? "/authdev" : "/auth");
|
||||
navigate("/auth");
|
||||
}}
|
||||
>
|
||||
{isLoggingOut ? <Loader /> : "Выйти"}
|
||||
|
@ -4,15 +4,19 @@ import close from "assets/icons/closeProjectPersons.svg";
|
||||
|
||||
import "./acceptModal.scss";
|
||||
|
||||
export const AcceptModal = ({ closeModal, agreeHandler }) => {
|
||||
export const AcceptModal = ({ title, closeModal, agreeHandler }) => {
|
||||
return (
|
||||
<div className="backDrop">
|
||||
<div className="acceptModal">
|
||||
<h3 className="acceptModal__title">
|
||||
Вы точно хотите переместить задачу в архив?
|
||||
</h3>
|
||||
<h3 className="acceptModal__title">{title}</h3>
|
||||
<div className="acceptModal__buttons">
|
||||
<button className="agree" onClick={agreeHandler}>
|
||||
<button
|
||||
className="agree"
|
||||
onClick={() => {
|
||||
agreeHandler();
|
||||
closeModal();
|
||||
}}
|
||||
>
|
||||
Да
|
||||
</button>
|
||||
<button className="cancel" onClick={closeModal}>
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
.acceptModal {
|
||||
border-radius: 20px;
|
||||
background: linear-gradient(180deg, #FFF 0%, #EBEBEB 100%);
|
||||
background: linear-gradient(180deg, #fff 0%, #ebebeb 100%);
|
||||
padding: 50px 34px 25px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -43,16 +43,18 @@
|
||||
}
|
||||
|
||||
.agree {
|
||||
background: #52B709;
|
||||
background: #52b709;
|
||||
}
|
||||
|
||||
.cancel {
|
||||
background: #B0BABF;
|
||||
background: #b0babf;
|
||||
}
|
||||
}
|
||||
|
||||
&__close {
|
||||
position: absolute;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
top: 15px;
|
||||
right: 22px;
|
||||
cursor: pointer;
|
||||
|
@ -17,7 +17,7 @@ export const ModalErrorLogin = ({ active, setActive, title }) => {
|
||||
setActive(false);
|
||||
}}
|
||||
>
|
||||
Попробовать еще раз
|
||||
Попробовать ещё раз
|
||||
</BaseButton>
|
||||
<span onClick={() => setActive(false)} className="exit"></span>
|
||||
</ModalLayout>
|
||||
|
@ -1,6 +1,11 @@
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { apiRequest } from "@api/request";
|
||||
|
||||
import { useNotification } from "@hooks/useNotification";
|
||||
|
||||
import BaseButton from "@components/Common/BaseButton/BaseButton";
|
||||
import { Loader } from "@components/Common/Loader/Loader";
|
||||
import ModalLayout from "@components/Common/ModalLayout/ModalLayout";
|
||||
|
||||
import anyMoment from "assets/icons/anyMoment.svg";
|
||||
@ -10,47 +15,216 @@ import telegramLogo from "assets/icons/tgLogo.svg";
|
||||
import "./modalRegistration.scss";
|
||||
|
||||
export const ModalRegistration = ({ active, setActive }) => {
|
||||
const [inputsValue, setInputsValue] = useState({
|
||||
userName: "",
|
||||
email: "",
|
||||
password: "",
|
||||
});
|
||||
|
||||
const [inputsError, setInputsError] = useState({
|
||||
name: false,
|
||||
email: false,
|
||||
password: false,
|
||||
});
|
||||
|
||||
const [loader, setLoader] = useState(false);
|
||||
|
||||
const validateEmail = (email) => {
|
||||
// регулярное выражение для проверки email
|
||||
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
|
||||
// возвращаем true, если email проходит проверку, и false, если нет
|
||||
return re.test(email);
|
||||
};
|
||||
|
||||
const { showNotification } = useNotification();
|
||||
|
||||
const validateForm = () => {
|
||||
if (inputsValue.password.length < 6) {
|
||||
setInputsError((prevValue) => ({ ...prevValue, password: true }));
|
||||
}
|
||||
if (inputsValue.userName.length < 2) {
|
||||
setInputsError((prevValue) => ({ ...prevValue, name: true }));
|
||||
}
|
||||
if (!validateEmail(inputsValue.email)) {
|
||||
setInputsError((prevValue) => ({ ...prevValue, email: true }));
|
||||
}
|
||||
if (
|
||||
inputsValue.password.length < 6 ||
|
||||
inputsValue.userName.length < 6 ||
|
||||
!validateEmail(inputsValue.email)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const submitHandler = () => {
|
||||
if (validateForm()) {
|
||||
return;
|
||||
}
|
||||
setLoader(true);
|
||||
apiRequest("/register/sign-up", {
|
||||
method: "POST",
|
||||
data: {
|
||||
username: inputsValue.userName,
|
||||
email: inputsValue.email,
|
||||
password: inputsValue.password,
|
||||
},
|
||||
}).then((data) => {
|
||||
setLoader(false);
|
||||
if (!data) {
|
||||
showNotification({
|
||||
show: true,
|
||||
text: "Аккаунт с таким логином или email уже существует",
|
||||
type: "error",
|
||||
});
|
||||
} else {
|
||||
closeModal();
|
||||
showNotification({
|
||||
show: true,
|
||||
text: "Аккаунт успешно создан",
|
||||
type: "success",
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
setInputsValue({
|
||||
userName: "",
|
||||
email: "",
|
||||
password: "",
|
||||
});
|
||||
setInputsError({
|
||||
name: false,
|
||||
email: false,
|
||||
password: false,
|
||||
});
|
||||
setActive(false);
|
||||
};
|
||||
return (
|
||||
<ModalLayout active={active} setActive={setActive} styles={"registration"}>
|
||||
<div className="registration-body__left">
|
||||
<ModalLayout active={active} setActive={closeModal} styles={"registration"}>
|
||||
<div className="registration-body__main">
|
||||
<h2>
|
||||
Подключайтесь к <p>itguild.</p>
|
||||
Подключайтесь к <p>ITguild</p>
|
||||
</h2>
|
||||
<p className="registration-body__left-desc">
|
||||
Зарегистрируйтесь и назначайте собеседования любым специалистам без
|
||||
задержек
|
||||
<p className="registration-body__main-desc">
|
||||
Зарегистрируйтесь и начните работу уже сегодня
|
||||
</p>
|
||||
|
||||
<div className="input-body">
|
||||
<div className="input-body__box">
|
||||
<h5>Ваше имя</h5>
|
||||
<input></input>
|
||||
<h5>E-mail</h5>
|
||||
<input></input>
|
||||
<div className="inputContainer">
|
||||
<h5>Ваше имя</h5>
|
||||
<input
|
||||
className={inputsError.name ? "error" : ""}
|
||||
onChange={(e) => {
|
||||
setInputsError({
|
||||
name: false,
|
||||
email: false,
|
||||
password: false,
|
||||
});
|
||||
setInputsValue((prevValue) => ({
|
||||
...prevValue,
|
||||
userName: e.target.value,
|
||||
}));
|
||||
}}
|
||||
value={inputsValue.userName}
|
||||
placeholder="Имя"
|
||||
/>
|
||||
{inputsError.name && <span>Минимум 2 символа</span>}
|
||||
</div>
|
||||
<div className="inputContainer">
|
||||
<h5>E-mail</h5>
|
||||
<input
|
||||
type="email"
|
||||
className={inputsError.email ? "error" : ""}
|
||||
onChange={(e) => {
|
||||
setInputsError({
|
||||
name: false,
|
||||
email: false,
|
||||
password: false,
|
||||
});
|
||||
setInputsValue((prevValue) => ({
|
||||
...prevValue,
|
||||
email: e.target.value,
|
||||
}));
|
||||
}}
|
||||
value={inputsValue.email}
|
||||
placeholder="Почта"
|
||||
/>
|
||||
{inputsError.email && <span>Введите корректный e-mail</span>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="input-body__box">
|
||||
<h5>Название компании</h5>
|
||||
<input></input>
|
||||
<h5>Пароль</h5>
|
||||
<input></input>
|
||||
<div className="inputContainer">
|
||||
<h5>Пароль</h5>
|
||||
<input
|
||||
className={inputsError.password ? "error" : ""}
|
||||
type="password"
|
||||
onChange={(e) => {
|
||||
setInputsError({
|
||||
name: false,
|
||||
email: false,
|
||||
password: false,
|
||||
});
|
||||
setInputsValue((prevValue) => ({
|
||||
...prevValue,
|
||||
password: e.target.value,
|
||||
}));
|
||||
}}
|
||||
value={inputsValue.password}
|
||||
placeholder="Пароль"
|
||||
/>
|
||||
{inputsError.password && <span>Минимум 6 символов</span>}
|
||||
</div>
|
||||
<div className="inputContainer">
|
||||
<h5>Повторите пароль</h5>
|
||||
<input
|
||||
className={inputsError.password ? "error" : ""}
|
||||
type="password"
|
||||
onChange={(e) => {
|
||||
setInputsError({
|
||||
name: false,
|
||||
email: false,
|
||||
password: false,
|
||||
});
|
||||
setInputsValue((prevValue) => ({
|
||||
...prevValue,
|
||||
password: e.target.value,
|
||||
}));
|
||||
}}
|
||||
value={inputsValue.password}
|
||||
placeholder="Пароль"
|
||||
/>
|
||||
{inputsError.password && <span>Минимум 6 символов</span>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="button-box">
|
||||
<BaseButton
|
||||
onClick={(e) => e.preventDefault()}
|
||||
styles={"button-box__submit"}
|
||||
>
|
||||
Отправить
|
||||
</BaseButton>
|
||||
<h5>
|
||||
У вас уже есть аккаунт? <p>Войти</p>
|
||||
</h5>
|
||||
{loader ? (
|
||||
<Loader style={"green"} />
|
||||
) : (
|
||||
<BaseButton
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
submitHandler();
|
||||
}}
|
||||
styles="button-box__submit"
|
||||
>
|
||||
Отправить
|
||||
</BaseButton>
|
||||
)}
|
||||
{/*<h5>*/}
|
||||
{/* У вас уже есть аккаунт? <p>Войти</p>*/}
|
||||
{/*</h5>*/}
|
||||
</div>
|
||||
</div>
|
||||
<div className="registration-body__right">
|
||||
<div className="registration-body__about">
|
||||
<h4>Отказ от специалиста в любой момент</h4>
|
||||
<div className="registration-body__right-text">
|
||||
<div className="registration-body__about-text">
|
||||
<img src={anyMoment}></img>
|
||||
<p>
|
||||
Поменяйте, откажитесь или возьмите еще специалиста в любой момент
|
||||
@ -58,7 +232,7 @@ export const ModalRegistration = ({ active, setActive }) => {
|
||||
</p>
|
||||
</div>
|
||||
<h4>100% постоплата</h4>
|
||||
<div className="registration-body__right-text">
|
||||
<div className="registration-body__about-text">
|
||||
<img src={doc}></img>
|
||||
<p>
|
||||
Договор не подразумевает какую‑либо оплату до того, как вы
|
||||
@ -66,12 +240,12 @@ export const ModalRegistration = ({ active, setActive }) => {
|
||||
</p>
|
||||
</div>
|
||||
<h4>Есть вопросы?</h4>
|
||||
<div className="registration-body__right-text">
|
||||
<div className="registration-body__about-text">
|
||||
<img src={telegramLogo}></img>
|
||||
<p>Напишите нам в Телеграм. Мы с удовольствием ответим!</p>
|
||||
</div>
|
||||
</div>
|
||||
<span onClick={() => setActive(false)} className="exit"></span>
|
||||
<span onClick={() => closeModal()} className="exit"></span>
|
||||
</ModalLayout>
|
||||
);
|
||||
};
|
||||
|
@ -6,12 +6,10 @@
|
||||
justify-content: space-between;
|
||||
border: 1px solid #dde2e4;
|
||||
border-radius: 8px;
|
||||
width: 1088px;
|
||||
height: 529px;
|
||||
|
||||
&-body {
|
||||
&__left {
|
||||
padding: 60px 0 30px 77px;
|
||||
&__main {
|
||||
padding-left: 30px;
|
||||
|
||||
h2 {
|
||||
font-weight: 500;
|
||||
@ -51,6 +49,7 @@
|
||||
font-weight: 400;
|
||||
font-size: 15px;
|
||||
line-height: 18px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
input {
|
||||
@ -59,9 +58,24 @@
|
||||
background: #eff2f7;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
margin-bottom: 35px;
|
||||
margin-bottom: 5px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.inputContainer {
|
||||
height: 81px;
|
||||
margin-bottom: 10px;
|
||||
max-width: 294px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: red;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.error {
|
||||
border: 1px solid red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,11 +86,16 @@
|
||||
|
||||
&__submit {
|
||||
width: 174px;
|
||||
height: 46px;
|
||||
height: 50px;
|
||||
font-size: 18px;
|
||||
margin-right: 55px;
|
||||
}
|
||||
|
||||
.disable {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
h5 {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
@ -92,14 +111,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__right {
|
||||
&__about {
|
||||
border-left: 1px solid #f1f1f1;
|
||||
padding: 80px 32px 46px 25px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
h4 {
|
||||
margin: 22px 0 22px 0;
|
||||
font-weight: 900;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
@ -120,9 +140,14 @@
|
||||
}
|
||||
|
||||
img {
|
||||
margin: 0 18px 20px 0;
|
||||
margin: 0 18px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loader {
|
||||
justify-content: start;
|
||||
left: 80px;
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export const ModalReset = ({ setModalReset }) => {
|
||||
<h3 className="modalReset__title">Восстановление доступа</h3>
|
||||
<div className="modalReset__input">
|
||||
<span>Укажите e-mail, для которого хотите восстановить пароль.</span>
|
||||
<input placeholder="email" />
|
||||
<input placeholder="e-mail" />
|
||||
</div>
|
||||
<button className="modalReset__submit">Восстановить</button>
|
||||
<img
|
||||
|
231
src/components/Modal/ModalResetPassword/ModalResetPassword.jsx
Normal file
231
src/components/Modal/ModalResetPassword/ModalResetPassword.jsx
Normal file
@ -0,0 +1,231 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { apiRequest } from "@api/request";
|
||||
|
||||
import { useNotification } from "@hooks/useNotification";
|
||||
|
||||
import { Loader } from "@components/Common/Loader/Loader";
|
||||
import ModalLayout from "@components/Common/ModalLayout/ModalLayout";
|
||||
|
||||
import arrow from "assets/icons/arrows/arrowCalendar.png";
|
||||
import close from "assets/icons/close.png";
|
||||
|
||||
import "./modalResetPassword.scss";
|
||||
|
||||
export const ModalResetPassword = ({ active, setActive }) => {
|
||||
const [step, setStep] = useState(false);
|
||||
const [inputsValue, setInputsValue] = useState({
|
||||
email: "",
|
||||
token: "",
|
||||
password: "",
|
||||
});
|
||||
|
||||
const [inputsError, setInputsError] = useState({
|
||||
email: false,
|
||||
password: false,
|
||||
token: false,
|
||||
});
|
||||
|
||||
const validateEmail = (email) => {
|
||||
// регулярное выражение для проверки email
|
||||
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
|
||||
// возвращаем true, если email проходит проверку, и false, если нет
|
||||
return re.test(email);
|
||||
};
|
||||
|
||||
const [loader, setLoader] = useState(false);
|
||||
|
||||
const resetInputsValue = () => {
|
||||
setInputsValue({
|
||||
email: "",
|
||||
token: "",
|
||||
password: "",
|
||||
});
|
||||
};
|
||||
|
||||
const { showNotification } = useNotification();
|
||||
const submitHandler = () => {
|
||||
if (!validateEmail(inputsValue.email)) {
|
||||
setInputsError((prevValue) => ({ ...prevValue, email: true }));
|
||||
return showNotification({
|
||||
show: true,
|
||||
text: "Введите корректный e-mail",
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
setLoader(true);
|
||||
apiRequest("/register/request-password-reset", {
|
||||
method: "POST",
|
||||
data: {
|
||||
email: inputsValue.email,
|
||||
},
|
||||
}).then((data) => {
|
||||
setLoader(false);
|
||||
if (data) {
|
||||
showNotification({
|
||||
show: true,
|
||||
text: "Письмо отправлено Вам на почту",
|
||||
type: "success",
|
||||
});
|
||||
setStep(true);
|
||||
}
|
||||
});
|
||||
};
|
||||
const resetPassword = () => {
|
||||
if (!inputsValue.password || !inputsValue.token) {
|
||||
setInputsError((prevValue) => ({
|
||||
...prevValue,
|
||||
password: true,
|
||||
token: true,
|
||||
}));
|
||||
return showNotification({
|
||||
show: true,
|
||||
text: "Введите данные",
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
if (inputsValue.password.length < 6) {
|
||||
setInputsError((prevValue) => ({ ...prevValue, password: true }));
|
||||
return;
|
||||
}
|
||||
setLoader(true);
|
||||
apiRequest("/register/reset-password", {
|
||||
method: "POST",
|
||||
data: {
|
||||
token: inputsValue.token,
|
||||
password: inputsValue.password,
|
||||
},
|
||||
}).then((data) => {
|
||||
setLoader(false);
|
||||
if (data.code === 0) {
|
||||
showNotification({
|
||||
show: true,
|
||||
text: "Введите корректные данные",
|
||||
type: "error",
|
||||
});
|
||||
} else {
|
||||
setActive(false);
|
||||
resetInputsValue();
|
||||
showNotification({
|
||||
show: true,
|
||||
text: "Пароль изменён",
|
||||
type: "success",
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
return (
|
||||
<ModalLayout active={active} setActive={setActive}>
|
||||
<div className="resetPassword">
|
||||
<img
|
||||
className="resetPassword__close"
|
||||
src={close}
|
||||
alt="close"
|
||||
onClick={() => setActive(false)}
|
||||
/>
|
||||
<h3 className="resetPassword__title">Восстановление пароля</h3>
|
||||
{!step ? (
|
||||
<div className="resetPassword__email">
|
||||
<h5>Введите ваш e-mail:</h5>
|
||||
<input
|
||||
type="email"
|
||||
onChange={(e) => {
|
||||
setInputsValue((prevValue) => ({
|
||||
...prevValue,
|
||||
email: e.target.value,
|
||||
}));
|
||||
setInputsError({
|
||||
email: false,
|
||||
password: false,
|
||||
token: false,
|
||||
});
|
||||
}}
|
||||
placeholder="E-mail"
|
||||
value={inputsValue.email}
|
||||
className={inputsError.email ? "error" : ""}
|
||||
/>
|
||||
{loader ? (
|
||||
<Loader style={"green"} />
|
||||
) : (
|
||||
<button
|
||||
className="resetPassword__btn"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
submitHandler();
|
||||
}}
|
||||
>
|
||||
Отправить
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="resetPassword__email">
|
||||
<img
|
||||
src={arrow}
|
||||
onClick={() => setStep(false)}
|
||||
className="resetPassword__email__arrow"
|
||||
/>
|
||||
<h5>Введите код подтверждения:</h5>
|
||||
<input
|
||||
type="text"
|
||||
onChange={(e) => {
|
||||
setInputsError({
|
||||
email: false,
|
||||
password: false,
|
||||
token: false,
|
||||
});
|
||||
setInputsValue((prevValue) => ({
|
||||
...prevValue,
|
||||
token: e.target.value,
|
||||
}));
|
||||
}}
|
||||
value={inputsValue.token}
|
||||
className={inputsError.token ? "error" : ""}
|
||||
placeholder="token"
|
||||
/>
|
||||
{inputsError.token && (
|
||||
<span className="warningText">Введите данные</span>
|
||||
)}
|
||||
<h5>Введите новый пароль:</h5>
|
||||
<input
|
||||
type="password"
|
||||
onChange={(e) => {
|
||||
setInputsValue((prevValue) => ({
|
||||
...prevValue,
|
||||
password: e.target.value,
|
||||
}));
|
||||
setInputsError({
|
||||
email: false,
|
||||
password: false,
|
||||
token: false,
|
||||
});
|
||||
}}
|
||||
placeholder="password"
|
||||
value={inputsValue.password}
|
||||
className={inputsError.password ? "error" : ""}
|
||||
/>
|
||||
{inputsError.password && (
|
||||
<span className="warningText">Минимум 6 символов</span>
|
||||
)}
|
||||
{loader ? (
|
||||
<Loader style={"green"} />
|
||||
) : (
|
||||
<button
|
||||
className="resetPassword__btn"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
resetPassword();
|
||||
}}
|
||||
>
|
||||
Отправить
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ModalLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModalResetPassword;
|
@ -0,0 +1,72 @@
|
||||
.resetPassword {
|
||||
width: 280px;
|
||||
position: relative;
|
||||
|
||||
&__close {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: -10px;
|
||||
}
|
||||
&__title {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
&__email {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
h5 {
|
||||
font-size: 16px;
|
||||
margin-bottom: 10px;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 10px !important;
|
||||
height: 40px !important;
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
|
||||
&__arrow {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
transform: rotate(180deg);
|
||||
left: -10px;
|
||||
top: -55px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&__btn {
|
||||
width: 100px;
|
||||
height: 35px;
|
||||
border-radius: 44px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #ffffff;
|
||||
background-image: linear-gradient(to top, #6aaf5c 0%, #52b709 100%), linear-gradient(36deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.16) 47%, rgba(255, 255, 255, 0.17) 50%, rgba(255, 255, 255, 0) 100%);
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
border: 2px solid #6aaf5c;
|
||||
transition: 0.3s;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.error {
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
.warningText {
|
||||
color: red;
|
||||
font-size: 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
background: #e1fccf;
|
||||
border-radius: 12px;
|
||||
transform: scale(0);
|
||||
bottom: -90px;
|
||||
bottom: -40px;
|
||||
right: -120px;
|
||||
|
||||
@media (max-width: 1050px) {
|
||||
|
@ -34,6 +34,7 @@ import arrowDown from "assets/icons/arrows/selectArrow.png";
|
||||
import calendarIcon from "assets/icons/calendar.svg";
|
||||
import category from "assets/icons/category.svg";
|
||||
import close from "assets/icons/closeProjectPersons.svg";
|
||||
import crossWhite from "assets/icons/crossWhite.svg";
|
||||
import del from "assets/icons/delete.svg";
|
||||
import edit from "assets/icons/edit.svg";
|
||||
import file from "assets/icons/fileModal.svg";
|
||||
@ -74,12 +75,13 @@ export const ModalTiсket = ({
|
||||
const [dropListOpen, setDropListOpen] = useState(false);
|
||||
const [dropListMembersOpen, setDropListMembersOpen] = useState(false);
|
||||
const [executor, setExecutor] = useState(task.executor);
|
||||
const [taskPriority, setTaskPriority] = useState(task.execution_priority);
|
||||
const [members, setMembers] = useState(task.taskUsers);
|
||||
const [taskTags, setTaskTags] = useState(task.mark);
|
||||
const [users, setUsers] = useState([]);
|
||||
const [timerStart, setTimerStart] = useState(false);
|
||||
const [timerInfo, setTimerInfo] = useState({});
|
||||
const [uploadedFile, setUploadedFile] = useState(null);
|
||||
// const [uploadedFile, setUploadedFile] = useState(null);
|
||||
const [currentTimerCount, setCurrentTimerCount] = useState({
|
||||
hours: 0,
|
||||
minute: 0,
|
||||
@ -93,7 +95,9 @@ export const ModalTiсket = ({
|
||||
const profileInfo = useSelector(getProfileInfo);
|
||||
const [acceptModalOpen, setAcceptModalOpen] = useState(false);
|
||||
const [selectTagsOpen, setSelectTagsOpen] = useState(false);
|
||||
const [selectPriorityOpen, setSelectPriorityOpen] = useState(false);
|
||||
const { showNotification } = useNotification();
|
||||
const [commentSendDisable, setCommentSendDisable] = useState(false);
|
||||
|
||||
function deleteTask() {
|
||||
apiRequest("/task/update-task", {
|
||||
@ -113,6 +117,27 @@ export const ModalTiсket = ({
|
||||
});
|
||||
}
|
||||
|
||||
const priority = {
|
||||
2: "Высокий",
|
||||
1: "Средний",
|
||||
0: "Низкий",
|
||||
};
|
||||
|
||||
const priorityTypes = [
|
||||
{
|
||||
name: "Высокий",
|
||||
key: 2,
|
||||
},
|
||||
{
|
||||
name: "Средний",
|
||||
key: 1,
|
||||
},
|
||||
{
|
||||
name: "Низкий",
|
||||
key: 0,
|
||||
},
|
||||
];
|
||||
|
||||
function archiveTask() {
|
||||
setAcceptModalOpen(true);
|
||||
}
|
||||
@ -144,6 +169,8 @@ export const ModalTiсket = ({
|
||||
}
|
||||
|
||||
function createComment() {
|
||||
if (!inputsValue.comment) return;
|
||||
setCommentSendDisable(true);
|
||||
apiRequest("/comment/create", {
|
||||
method: "POST",
|
||||
data: {
|
||||
@ -153,6 +180,7 @@ export const ModalTiсket = ({
|
||||
},
|
||||
}).then((res) => {
|
||||
let newComment = res;
|
||||
setCommentSendDisable(false);
|
||||
newComment.created_at = new Date();
|
||||
newComment.subComments = [];
|
||||
setInputsValue((prevValue) => ({ ...prevValue, comment: "" }));
|
||||
@ -257,6 +285,19 @@ export const ModalTiсket = ({
|
||||
});
|
||||
}
|
||||
|
||||
function updateTaskPriority(key) {
|
||||
setSelectPriorityOpen(false);
|
||||
apiRequest("/task/update-task", {
|
||||
method: "PUT",
|
||||
data: {
|
||||
task_id: task.id,
|
||||
execution_priority: key,
|
||||
},
|
||||
}).then(() => {
|
||||
dispatch(setProjectBoardFetch(projectId));
|
||||
});
|
||||
}
|
||||
|
||||
function addMember(person) {
|
||||
apiRequest("/task/add-user-to-task", {
|
||||
method: "POST",
|
||||
@ -285,6 +326,7 @@ export const ModalTiсket = ({
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
initListeners();
|
||||
apiRequest(
|
||||
`/comment/get-by-entity?entity_type=2&entity_id=${task.id}`
|
||||
).then((res) => {
|
||||
@ -373,25 +415,26 @@ export const ModalTiсket = ({
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
setUploadedFile(data);
|
||||
// setUploadedFile(data);
|
||||
attachFile(data[0].id);
|
||||
}
|
||||
|
||||
function deleteLoadedFile() {
|
||||
setUploadedFile(null);
|
||||
}
|
||||
// function deleteLoadedFile() {
|
||||
// setUploadedFile(null);
|
||||
// }
|
||||
|
||||
function attachFile() {
|
||||
function attachFile(id) {
|
||||
apiRequest("/file/attach", {
|
||||
method: "POST",
|
||||
data: {
|
||||
file_id: uploadedFile[0].id,
|
||||
file_id: id,
|
||||
entity_type: 2,
|
||||
entity_id: task.id,
|
||||
status: 1,
|
||||
},
|
||||
}).then((res) => {
|
||||
setTaskFiles((prevValue) => [...prevValue, res]);
|
||||
setUploadedFile(null);
|
||||
// setUploadedFile(null);
|
||||
});
|
||||
}
|
||||
|
||||
@ -502,15 +545,59 @@ export const ModalTiсket = ({
|
||||
setAcceptModalOpen(false);
|
||||
}
|
||||
|
||||
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("button-add-worker") ||
|
||||
div.classList.contains("dropdownList"))
|
||||
)
|
||||
) {
|
||||
setDropListOpen(false);
|
||||
setDropListMembersOpen(false);
|
||||
}
|
||||
|
||||
if (
|
||||
event &&
|
||||
!path.find(
|
||||
(div) =>
|
||||
div.classList &&
|
||||
(div.classList.contains("deadLine") ||
|
||||
div.classList.contains("react-datepicker-popper"))
|
||||
)
|
||||
) {
|
||||
setDatePickerOpen(false);
|
||||
}
|
||||
|
||||
if (
|
||||
event &&
|
||||
!path.find(
|
||||
(div) =>
|
||||
div.classList &&
|
||||
(div.classList.contains("tags") ||
|
||||
div.classList.contains("tags__dropDown"))
|
||||
)
|
||||
) {
|
||||
setSelectTagsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={active ? "modal-tiket active" : "modal-tiket"}
|
||||
onClick={() => setActive(false)}
|
||||
onClick={(e) => {
|
||||
if (e.target.className.includes("modal-tiket")) setActive(false);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="modal-tiket__content"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="modal-tiket__content">
|
||||
<div className="content">
|
||||
<h3 className="title-project">
|
||||
<img src={category} className="title-project__category"></img>
|
||||
@ -586,24 +673,24 @@ export const ModalTiсket = ({
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{uploadedFile && (
|
||||
<div className="fileLoaded">
|
||||
{uploadedFile.map((file) => {
|
||||
return (
|
||||
<div className="loadedFile" key={file.id}>
|
||||
<img src={backendImg(file.url)} alt="img" key={file.id} />
|
||||
<div
|
||||
className="deleteFile"
|
||||
onClick={() => deleteLoadedFile(file)}
|
||||
>
|
||||
<img src={close} alt="delete" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<button onClick={attachFile}>Загрузить</button>
|
||||
</div>
|
||||
)}
|
||||
{/*{uploadedFile && (*/}
|
||||
{/* <div className="fileLoaded">*/}
|
||||
{/* {uploadedFile.map((file) => {*/}
|
||||
{/* return (*/}
|
||||
{/* <div className="loadedFile" key={file.id}>*/}
|
||||
{/* <img src={backendImg(file.url)} alt="img" key={file.id} />*/}
|
||||
{/* <div*/}
|
||||
{/* className="deleteFile"*/}
|
||||
{/* onClick={() => deleteLoadedFile(file)}*/}
|
||||
{/* >*/}
|
||||
{/* <img src={close} alt="delete" />*/}
|
||||
{/* </div>*/}
|
||||
{/* </div>*/}
|
||||
{/* );*/}
|
||||
{/* })}*/}
|
||||
{/* <button onClick={attachFile}>Загрузить</button>*/}
|
||||
{/* </div>*/}
|
||||
{/*)}*/}
|
||||
<div className="content__communication">
|
||||
{/*<p className="tasks">*/}
|
||||
{/* <button*/}
|
||||
@ -646,7 +733,11 @@ export const ModalTiсket = ({
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<img src={send} onClick={createComment}></img>
|
||||
<img
|
||||
className={commentSendDisable ? "disable" : ""}
|
||||
src={send}
|
||||
onClick={createComment}
|
||||
></img>
|
||||
</div>
|
||||
<div className="comments__list">
|
||||
{comments.map((comment) => {
|
||||
@ -855,7 +946,7 @@ export const ModalTiсket = ({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="workers_box-bottom">
|
||||
<div className="workers_box-tag">
|
||||
<div className="tags">
|
||||
<div className="tags__selected">
|
||||
{taskTags.map((tag) => {
|
||||
@ -867,7 +958,7 @@ export const ModalTiсket = ({
|
||||
>
|
||||
<p>{tag.slug}</p>
|
||||
<img
|
||||
src={close}
|
||||
src={crossWhite}
|
||||
className="delete"
|
||||
alt="delete"
|
||||
onClick={() => deleteTagFromTask(tag.id)}
|
||||
@ -889,12 +980,12 @@ export const ModalTiсket = ({
|
||||
</div>
|
||||
{selectTagsOpen && (
|
||||
<div className="tags__dropDown">
|
||||
<img
|
||||
{/* <img
|
||||
onClick={() => setSelectTagsOpen(false)}
|
||||
className="tags__dropDown__close"
|
||||
src={close}
|
||||
alt="close"
|
||||
/>
|
||||
/> */}
|
||||
{correctProjectTags.map((tag) => {
|
||||
return (
|
||||
<div
|
||||
@ -913,6 +1004,43 @@ export const ModalTiсket = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="workers_box-priority">
|
||||
<div
|
||||
className="priority__name"
|
||||
onClick={() => setSelectPriorityOpen(!selectPriorityOpen)}
|
||||
>
|
||||
<span>
|
||||
{typeof taskPriority === "number"
|
||||
? priority[taskPriority]
|
||||
: "Выберите приоритет"}
|
||||
</span>
|
||||
<img
|
||||
className={selectPriorityOpen ? "open" : ""}
|
||||
src={arrowDown}
|
||||
alt="arrow"
|
||||
/>
|
||||
</div>
|
||||
{selectPriorityOpen && (
|
||||
<div className="priority__dropDown">
|
||||
{priorityTypes.map((item) => {
|
||||
return (
|
||||
<div
|
||||
className="priority__dropDown__item"
|
||||
key={item.key}
|
||||
onClick={() => {
|
||||
setTaskPriority(item.key);
|
||||
updateTaskPriority(item.key);
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="workers_box-bottom">
|
||||
<div
|
||||
className={editOpen ? "edit" : ""}
|
||||
onClick={() => {
|
||||
@ -958,6 +1086,7 @@ export const ModalTiсket = ({
|
||||
</div>
|
||||
{acceptModalOpen && (
|
||||
<AcceptModal
|
||||
title={"Вы точно хотите переместить задачу в архив?"}
|
||||
closeModal={closeAcceptModal}
|
||||
agreeHandler={deleteTask}
|
||||
/>
|
||||
|
@ -20,7 +20,28 @@
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
max-height: 750px;
|
||||
max-height: 700px;
|
||||
// overflow-y: auto;
|
||||
|
||||
@media (max-width: 880px) {
|
||||
max-height: none;
|
||||
overflow-y: inherit;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #cbd9f9;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #c5c0c6;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
@ -34,7 +55,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
color: #1458DD;
|
||||
color: #1458dd;
|
||||
font-weight: 700;
|
||||
font-size: 22px;
|
||||
|
||||
@ -125,27 +146,6 @@
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.taskDescription {
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #cbd9f9;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #c5c0c6;
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.fullName {
|
||||
max-width: 800px;
|
||||
}
|
||||
@ -156,9 +156,9 @@
|
||||
|
||||
.comments__list {
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
height: 190px;
|
||||
flex-direction: column;
|
||||
max-height: 215px;
|
||||
overflow: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
@ -360,24 +360,8 @@
|
||||
column-gap: 25px;
|
||||
row-gap: 20px;
|
||||
margin-top: 33px;
|
||||
max-height: 170px;
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #cbd9f9;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #c5c0c6;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.taskFile {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
@ -584,6 +568,11 @@
|
||||
margin-right: 18px;
|
||||
}
|
||||
|
||||
.disable {
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
//&:focus-within {
|
||||
// border: 1px solid #0000004d;
|
||||
//}
|
||||
@ -970,6 +959,199 @@
|
||||
border-bottom: 1px solid #f1f1f1;
|
||||
}
|
||||
|
||||
&-tag {
|
||||
.tags {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
|
||||
row-gap: 15px;
|
||||
padding: 22px 40px 0 40px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&__selected {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
max-width: 200px;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
padding: 8px 12px;
|
||||
border-radius: 35px;
|
||||
align-items: center;
|
||||
column-gap: 8px;
|
||||
cursor: default;
|
||||
|
||||
p {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
text-decoration: none !important;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.delete {
|
||||
cursor: pointer;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__select {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 42px;
|
||||
border-radius: 8px;
|
||||
background: #ddd;
|
||||
padding: 8px 12px 9px 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
img {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
&__dropDown {
|
||||
position: absolute;
|
||||
padding: 15px 9px 10px;
|
||||
background: #ebebeb;
|
||||
max-height: 350px;
|
||||
overflow-y: auto;
|
||||
width: 258px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e4e4e4;
|
||||
top: 105%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
row-gap: 6px;
|
||||
z-index: 10;
|
||||
|
||||
&__close {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
.tagItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 42px;
|
||||
cursor: pointer;
|
||||
column-gap: 8px;
|
||||
padding: 5px 5px 5px 14px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
|
||||
p {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
line-height: 20px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
span {
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&__noItem {
|
||||
line-height: 20px;
|
||||
font-size: 15px;
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
text-decoration: none !important;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-priority {
|
||||
position: relative;
|
||||
padding: 10px 40px 0 40px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 10px;
|
||||
.priority {
|
||||
&__name {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 42px;
|
||||
border-radius: 8px;
|
||||
background: #ddd;
|
||||
padding: 8px 12px 9px 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
img {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
&__dropDown {
|
||||
position: absolute;
|
||||
padding: 5px;
|
||||
background: #ebebeb;
|
||||
width: 30%;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e4e4e4;
|
||||
top: 105%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
row-gap: 6px;
|
||||
z-index: 10;
|
||||
|
||||
&__item {
|
||||
width: 66px;
|
||||
font-size: 15px;
|
||||
transition: 0.4s;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
transition: 0.4s;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-bottom {
|
||||
padding: 10px 110px 15px 56px;
|
||||
font-weight: 400;
|
||||
@ -991,129 +1173,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
row-gap: 5px;
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #e0e0e0;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&__selected {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
max-width: 200px;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
padding: 3px 5px 3px 8px;
|
||||
border-radius: 8px;
|
||||
align-items: center;
|
||||
column-gap: 8px;
|
||||
cursor: default;
|
||||
|
||||
p {
|
||||
font-weight: 600;
|
||||
text-decoration: none !important;
|
||||
font-size: 15px;
|
||||
margin: 0;
|
||||
line-height: 15px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.delete {
|
||||
cursor: pointer;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__select {
|
||||
width: 100%;
|
||||
height: 25px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid gray;
|
||||
padding: 5px 8px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
img {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
&__dropDown {
|
||||
position: absolute;
|
||||
padding: 15px 10px 10px;
|
||||
background: white;
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e4e4e4;
|
||||
top: 110%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 4px;
|
||||
z-index: 10;
|
||||
|
||||
&__close {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
.tagItem {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
column-gap: 8px;
|
||||
padding: 5px;
|
||||
border: 1px solid #ececec;
|
||||
border-radius: 8px;
|
||||
justify-content: space-between;
|
||||
|
||||
p {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
line-height: 20px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
span {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
&__noItem {
|
||||
line-height: 20px;
|
||||
font-size: 15px;
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
text-decoration: none !important;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.edit {
|
||||
background: #52b709;
|
||||
border-radius: 50px;
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
deletePersonOnProject,
|
||||
getBoarderLoader,
|
||||
modalToggle,
|
||||
setProjectBoardFetch,
|
||||
setToggleTab,
|
||||
} from "@redux/projectsTrackerSlice";
|
||||
|
||||
@ -29,6 +30,7 @@ import { useNotification } from "@hooks/useNotification";
|
||||
import { getCorrectDate } from "@components/Calendar/calendarHelper";
|
||||
import { Footer } from "@components/Common/Footer/Footer";
|
||||
import { Loader } from "@components/Common/Loader/Loader";
|
||||
import FileTracker from "@components/FileTracker/FileTracker";
|
||||
import AcceptModal from "@components/Modal/AcceptModal/AcceptModal";
|
||||
import TrackerModal from "@components/Modal/Tracker/TrackerModal/TrackerModal";
|
||||
import { Navigation } from "@components/Navigation/Navigation";
|
||||
@ -38,6 +40,7 @@ import TrackerTaskComment from "@components/TrackerTaskComment/TrackerTaskCommen
|
||||
|
||||
import arrow from "assets/icons/arrows/arrowCalendar.png";
|
||||
import arrowStart from "assets/icons/arrows/arrowStart.png";
|
||||
import arrowDown from "assets/icons/arrows/selectArrow.png";
|
||||
import calendarIcon from "assets/icons/calendar.svg";
|
||||
import close from "assets/icons/close.png";
|
||||
import fileDelete from "assets/icons/closeProjectPersons.svg";
|
||||
@ -87,14 +90,43 @@ export const TicketFullScreen = () => {
|
||||
const [startDate, setStartDate] = useState(null);
|
||||
const [uploadedFile, setUploadedFile] = useState(null);
|
||||
const [taskFiles, setTaskFiles] = useState([]);
|
||||
const [taskPriority, setTaskPriority] = useState("");
|
||||
const [acceptModalOpen, setAcceptModalOpen] = useState(false);
|
||||
const [taskTags, setTaskTags] = useState([]);
|
||||
const [selectTagsOpen, setSelectTagsOpen] = useState(false);
|
||||
const [selectPriorityOpen, setSelectPriorityOpen] = useState(false);
|
||||
const [correctProjectTags, setCorrectProjectTags] = useState([]);
|
||||
const { showNotification } = useNotification();
|
||||
const [commentSendDisable, setCommentSendDisable] = useState(false);
|
||||
|
||||
const priority = {
|
||||
2: "Высокий",
|
||||
1: "Средний",
|
||||
0: "Низкий",
|
||||
};
|
||||
|
||||
const priorityTypes = [
|
||||
{
|
||||
name: "Высокий",
|
||||
key: 2,
|
||||
},
|
||||
{
|
||||
name: "Средний",
|
||||
key: 1,
|
||||
},
|
||||
{
|
||||
name: "Низкий",
|
||||
key: 0,
|
||||
},
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
initListeners();
|
||||
apiRequest(`/task/get-task?task_id=${ticketId.id}&expand=mark`).then(
|
||||
(taskInfo) => {
|
||||
setTaskInfo(taskInfo);
|
||||
setDeadLine(taskInfo.dead_line);
|
||||
setTaskPriority(taskInfo.execution_priority);
|
||||
setStartDate(
|
||||
taskInfo.dead_line ? new Date(taskInfo.dead_line) : new Date()
|
||||
);
|
||||
@ -103,6 +135,7 @@ export const TicketFullScreen = () => {
|
||||
description: taskInfo.description,
|
||||
comment: "",
|
||||
});
|
||||
setTaskTags(taskInfo.mark);
|
||||
apiRequest(
|
||||
`/comment/get-by-entity?entity_type=2&entity_id=${taskInfo.id}`
|
||||
).then((res) => {
|
||||
@ -148,7 +181,7 @@ export const TicketFullScreen = () => {
|
||||
});
|
||||
});
|
||||
apiRequest(
|
||||
`/project/get-project?project_id=${taskInfo.project_id}&expand=columns`
|
||||
`/project/get-project?project_id=${taskInfo.project_id}&expand=columns,mark`
|
||||
).then((res) => {
|
||||
setProjectInfo(res);
|
||||
setCorrectProjectUsers(res.projectUsers);
|
||||
@ -158,6 +191,18 @@ export const TicketFullScreen = () => {
|
||||
);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let tagIds = taskTags.map((tag) => tag.id);
|
||||
if (projectInfo.mark) {
|
||||
setCorrectProjectTags(
|
||||
projectInfo.mark.reduce((acc, cur) => {
|
||||
if (!tagIds.includes(cur.id)) acc.push(cur);
|
||||
return acc;
|
||||
}, [])
|
||||
);
|
||||
}
|
||||
}, [taskTags, projectInfo]);
|
||||
|
||||
function deleteTask() {
|
||||
apiRequest("/task/update-task", {
|
||||
method: "PUT",
|
||||
@ -200,6 +245,8 @@ export const TicketFullScreen = () => {
|
||||
}
|
||||
|
||||
function createComment() {
|
||||
if (!inputsValue.comment) return;
|
||||
setCommentSendDisable(true);
|
||||
apiRequest("/comment/create", {
|
||||
method: "POST",
|
||||
data: {
|
||||
@ -209,6 +256,7 @@ export const TicketFullScreen = () => {
|
||||
},
|
||||
}).then((res) => {
|
||||
let newComment = res;
|
||||
setCommentSendDisable(false);
|
||||
newComment.created_at = new Date();
|
||||
newComment.subComments = [];
|
||||
setInputsValue((prevValue) => ({ ...prevValue, comment: "" }));
|
||||
@ -461,26 +509,127 @@ export const TicketFullScreen = () => {
|
||||
setUploadedFile(null);
|
||||
}
|
||||
|
||||
function deleteFile(file) {
|
||||
apiRequest("/file/detach", {
|
||||
method: "DELETE",
|
||||
function updateTaskPriority(key) {
|
||||
setSelectPriorityOpen(false);
|
||||
apiRequest("/task/update-task", {
|
||||
method: "PUT",
|
||||
data: {
|
||||
file_id: file.id,
|
||||
entity_type: 2,
|
||||
entity_id: taskInfo.id,
|
||||
status: 0,
|
||||
task_id: taskInfo.id,
|
||||
execution_priority: key,
|
||||
},
|
||||
}).then(() => {
|
||||
setTaskFiles((prevValue) =>
|
||||
prevValue.filter((item) => item.id !== file.id)
|
||||
);
|
||||
});
|
||||
}).then(() => {});
|
||||
}
|
||||
|
||||
// function deleteFile(file) {
|
||||
// apiRequest("/file/detach", {
|
||||
// method: "DELETE",
|
||||
// data: {
|
||||
// file_id: file.id,
|
||||
// entity_type: 2,
|
||||
// entity_id: taskInfo.id,
|
||||
// status: 0,
|
||||
// },
|
||||
// }).then(() => {
|
||||
// setTaskFiles((prevValue) =>
|
||||
// prevValue.filter((item) => item.id !== file.id)
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
|
||||
function deleteFile(file) {
|
||||
setTaskFiles((prevValue) =>
|
||||
prevValue.filter((item) => item.id !== file.id)
|
||||
);
|
||||
}
|
||||
|
||||
function closeAcceptModal() {
|
||||
setAcceptModalOpen(false);
|
||||
}
|
||||
|
||||
function deleteTagFromTask(tagId) {
|
||||
apiRequest("/mark/detach", {
|
||||
method: "DELETE",
|
||||
data: {
|
||||
mark_id: tagId,
|
||||
entity_type: 2,
|
||||
entity_id: taskInfo.id,
|
||||
},
|
||||
}).then(() => {
|
||||
setTaskTags((prevValue) => prevValue.filter((tag) => tag.id !== tagId));
|
||||
});
|
||||
}
|
||||
|
||||
function addTagToTask(tagId) {
|
||||
apiRequest("/mark/attach", {
|
||||
method: "POST",
|
||||
data: {
|
||||
mark_id: tagId,
|
||||
entity_type: 2,
|
||||
entity_id: taskInfo.id,
|
||||
},
|
||||
}).then((data) => {
|
||||
setSelectTagsOpen(false);
|
||||
setTaskTags((prevValue) => [...prevValue, data.mark]);
|
||||
});
|
||||
}
|
||||
|
||||
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("button-add-worker") ||
|
||||
div.classList.contains("dropdownList"))
|
||||
)
|
||||
) {
|
||||
setDropListOpen(false);
|
||||
setDropListMembersOpen(false);
|
||||
}
|
||||
|
||||
if (
|
||||
event &&
|
||||
!path.find(
|
||||
(div) =>
|
||||
div.classList &&
|
||||
(div.classList.contains("deadLine") ||
|
||||
div.classList.contains("react-datepicker-popper"))
|
||||
)
|
||||
) {
|
||||
setDatePickerOpen(false);
|
||||
}
|
||||
|
||||
if (
|
||||
event &&
|
||||
!path.find(
|
||||
(div) =>
|
||||
div.classList &&
|
||||
(div.classList.contains("tags") ||
|
||||
div.classList.contains("tags__dropDown"))
|
||||
)
|
||||
) {
|
||||
setSelectTagsOpen(false);
|
||||
}
|
||||
|
||||
if (
|
||||
event &&
|
||||
!path.find(
|
||||
(div) =>
|
||||
div.classList &&
|
||||
(div.classList.contains("addPerson") ||
|
||||
div.classList.contains("persons__list"))
|
||||
)
|
||||
) {
|
||||
setPersonListOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="ticket-full-screen">
|
||||
<ProfileHeader />
|
||||
@ -623,9 +772,12 @@ export const TicketFullScreen = () => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Link to={`/profile/tracker`} className="link">
|
||||
<Link
|
||||
to={`/tracker/project/${taskInfo.project_id}`}
|
||||
className="link"
|
||||
>
|
||||
<div className="tasks__head__back">
|
||||
<p>Вернуться на проекты</p>
|
||||
<p>Вернуться на проект</p>
|
||||
<img src={arrow} alt="arrow" />
|
||||
</div>
|
||||
</Link>
|
||||
@ -691,19 +843,12 @@ export const TicketFullScreen = () => {
|
||||
<div className="task__files filesFullScreen">
|
||||
{taskFiles.map((file) => {
|
||||
return (
|
||||
<div className="taskFile" key={file.id}>
|
||||
<img
|
||||
className="imgFile"
|
||||
src={backendImg(file.file?.url)}
|
||||
alt="img"
|
||||
/>
|
||||
<div
|
||||
className="deleteFile"
|
||||
onClick={() => deleteFile(file)}
|
||||
>
|
||||
<img src={fileDelete} alt="delete" />
|
||||
</div>
|
||||
</div>
|
||||
<FileTracker
|
||||
key={file.id}
|
||||
file={file}
|
||||
setDeletedTask={deleteFile}
|
||||
taskId={taskInfo.id}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
@ -776,7 +921,12 @@ export const TicketFullScreen = () => {
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<img src={send} onClick={createComment} alt="send"></img>
|
||||
<img
|
||||
className={commentSendDisable ? "disable" : ""}
|
||||
src={send}
|
||||
onClick={createComment}
|
||||
alt="send"
|
||||
></img>
|
||||
</div>
|
||||
<div className="comments__list">
|
||||
{comments.map((comment) => {
|
||||
@ -989,7 +1139,101 @@ export const TicketFullScreen = () => {
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="workers_box-tag">
|
||||
<div className="tags">
|
||||
<div className="tags__selected">
|
||||
{taskTags.map((tag) => {
|
||||
return (
|
||||
<div
|
||||
className="tags__selected__item"
|
||||
key={tag.id}
|
||||
style={{ background: tag.color }}
|
||||
>
|
||||
<p>{tag.slug}</p>
|
||||
<img
|
||||
src={close}
|
||||
className="delete"
|
||||
alt="delete"
|
||||
onClick={() => deleteTagFromTask(tag.id)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div
|
||||
className="tags__select"
|
||||
onClick={() => setSelectTagsOpen(!selectTagsOpen)}
|
||||
>
|
||||
<span>Выберите тег</span>
|
||||
<img
|
||||
className={selectTagsOpen ? "open" : ""}
|
||||
src={arrowDown}
|
||||
alt="arrow"
|
||||
/>
|
||||
</div>
|
||||
{selectTagsOpen && (
|
||||
<div className="tags__dropDown">
|
||||
<img
|
||||
onClick={() => setSelectTagsOpen(false)}
|
||||
className="tags__dropDown__close"
|
||||
src={close}
|
||||
alt="close"
|
||||
/>
|
||||
{correctProjectTags.map((tag) => {
|
||||
return (
|
||||
<div
|
||||
className="tagItem"
|
||||
key={tag.id}
|
||||
onClick={() => addTagToTask(tag.id)}
|
||||
>
|
||||
<p>{tag.slug}</p>
|
||||
<span style={{ background: tag.color }} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{!Boolean(correctProjectTags.length) && (
|
||||
<p className="tags__dropDown__noItem">Нет тегов</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="workers_box-priority">
|
||||
<div
|
||||
className="priority__name"
|
||||
onClick={() => setSelectPriorityOpen(!selectPriorityOpen)}
|
||||
>
|
||||
<span>
|
||||
{typeof taskPriority === "number"
|
||||
? priority[taskPriority]
|
||||
: "Выберите приоритет"}
|
||||
</span>
|
||||
<img
|
||||
className={selectPriorityOpen ? "open" : ""}
|
||||
src={arrowDown}
|
||||
alt="arrow"
|
||||
/>
|
||||
</div>
|
||||
{selectPriorityOpen && (
|
||||
<div className="priority__dropDown">
|
||||
{priorityTypes.map((item) => {
|
||||
return (
|
||||
<div
|
||||
className="priority__dropDown__item"
|
||||
key={item.key}
|
||||
onClick={() => {
|
||||
setTaskPriority(item.key);
|
||||
updateTaskPriority(item.key);
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="workers_box-bottom">
|
||||
<div
|
||||
className={editOpen ? "edit" : ""}
|
||||
@ -1039,7 +1283,11 @@ export const TicketFullScreen = () => {
|
||||
)}
|
||||
</div>
|
||||
{acceptModalOpen && (
|
||||
<AcceptModal closeModal={closeAcceptModal} agreeHandler={deleteTask} />
|
||||
<AcceptModal
|
||||
title={"Вы точно хотите переместить задачу в архив?"}
|
||||
closeModal={closeAcceptModal}
|
||||
agreeHandler={deleteTask}
|
||||
/>
|
||||
)}
|
||||
<Footer />
|
||||
</section>
|
||||
|
@ -32,10 +32,11 @@ import { getCorrectDate } from "@components/Calendar/calendarHelper";
|
||||
import BaseButton from "@components/Common/BaseButton/BaseButton";
|
||||
import ModalLayout from "@components/Common/ModalLayout/ModalLayout";
|
||||
|
||||
import arrowCreateTask from "assets/icons/arrows/arrowCreateTask.svg";
|
||||
import arrowRight from "assets/icons/arrows/arrowRightCreateTask.svg";
|
||||
import arrowDown from "assets/icons/arrows/selectArrow.png";
|
||||
import close from "assets/icons/close.png";
|
||||
import calendarImg from "assets/icons/createTaskCalendar.svg";
|
||||
import crossWhite from "assets/icons/crossWhite.svg";
|
||||
import avatarMok from "assets/images/avatarMok.png";
|
||||
|
||||
import "./trackerModal.scss";
|
||||
@ -51,6 +52,7 @@ export const TrackerModal = ({
|
||||
projectId,
|
||||
priorityTask,
|
||||
projectUsers,
|
||||
projectMarks,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const projectBoard = useSelector(getProjectBoard);
|
||||
@ -76,6 +78,11 @@ export const TrackerModal = ({
|
||||
);
|
||||
const [selectExecutorTaskOpen, setSelectExecutorTaskOpen] = useState(false);
|
||||
const [correctProjectUsers, setCorrectProjectUsers] = useState([]);
|
||||
const [correctProjectTags, setCorrectProjectTags] = useState([]);
|
||||
const [taskTags, setTaskTags] = useState([]);
|
||||
const [selectTagsOpen, setSelectTagsOpen] = useState(false);
|
||||
const [selectedPriority, setSelectedPriority] = useState(null);
|
||||
const [selectPriority, setSelectPriority] = useState(false);
|
||||
const [selectColumnPriorityOpen, setSelectColumnPriorityOpen] =
|
||||
useState(false);
|
||||
const { showNotification } = useNotification();
|
||||
@ -83,6 +90,21 @@ export const TrackerModal = ({
|
||||
const [datePickerOpen, setDatePickerOpen] = useState(false);
|
||||
const [startDate, setStartDate] = useState(new Date());
|
||||
|
||||
const priority = [
|
||||
{
|
||||
name: "Высокий",
|
||||
key: 2,
|
||||
},
|
||||
{
|
||||
name: "Средний",
|
||||
key: 1,
|
||||
},
|
||||
{
|
||||
name: "Низкий",
|
||||
key: 0,
|
||||
},
|
||||
];
|
||||
|
||||
function createTab() {
|
||||
if (!valueColumn) {
|
||||
showNotification({ show: true, text: "Введите название", type: "error" });
|
||||
@ -124,6 +146,7 @@ export const TrackerModal = ({
|
||||
status: 1,
|
||||
user_id: localStorage.getItem("id"),
|
||||
column_id: selectedTab,
|
||||
execution_priority: selectedPriority ? selectedPriority.key : "",
|
||||
priority: priorityTask,
|
||||
dead_line: deadLineDate ? getCorrectRequestDate(deadLineDate) : "",
|
||||
},
|
||||
@ -135,6 +158,18 @@ export const TrackerModal = ({
|
||||
type: "error",
|
||||
});
|
||||
} else {
|
||||
for (let i = 0; i < taskTags.length; i++) {
|
||||
apiRequest("/mark/attach", {
|
||||
method: "POST",
|
||||
data: {
|
||||
mark_id: taskTags[i].id,
|
||||
entity_type: 2,
|
||||
entity_id: res.id,
|
||||
},
|
||||
}).then(() => {
|
||||
setTaskTags([]);
|
||||
});
|
||||
}
|
||||
if (selectedExecutorTask.user_id) {
|
||||
apiRequest("/task/update-task", {
|
||||
method: "PUT",
|
||||
@ -148,6 +183,7 @@ export const TrackerModal = ({
|
||||
setValueTiket("");
|
||||
setDescriptionTicket("");
|
||||
setSelectedExecutorTask("Выберите исполнителя задачи");
|
||||
setSelectedPriority(null);
|
||||
});
|
||||
} else {
|
||||
setActive(false);
|
||||
@ -304,8 +340,53 @@ export const TrackerModal = ({
|
||||
} else {
|
||||
setCorrectProjectUsers(projectUsers);
|
||||
}
|
||||
initListeners();
|
||||
}, [active]);
|
||||
|
||||
useEffect(() => {
|
||||
let tagIds = taskTags.map((tag) => tag.id);
|
||||
if (projectMarks) {
|
||||
setCorrectProjectTags(
|
||||
projectMarks.reduce((acc, cur) => {
|
||||
if (!tagIds.includes(cur.id)) acc.push(cur);
|
||||
return acc;
|
||||
}, [])
|
||||
);
|
||||
}
|
||||
}, [taskTags, projectMarks]);
|
||||
|
||||
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("tags__selected__name") ||
|
||||
div.classList.contains("tags__dropDown"))
|
||||
)
|
||||
) {
|
||||
setSelectTagsOpen(false);
|
||||
}
|
||||
|
||||
if (
|
||||
event &&
|
||||
!path.find(
|
||||
(div) =>
|
||||
div.classList &&
|
||||
(div.classList.contains("select__executor") ||
|
||||
div.classList.contains("select__executor__dropDown"))
|
||||
)
|
||||
) {
|
||||
setSelectExecutorTaskOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalLayout
|
||||
active={active}
|
||||
@ -328,7 +409,7 @@ export const TrackerModal = ({
|
||||
{/* />*/}
|
||||
{/*</div>*/}
|
||||
<p className="selectPerson__info">
|
||||
Выберите пользователя в проекте или добавьте по e- mail
|
||||
Выберите пользователя в проекте или добавьте по e-mail
|
||||
</p>
|
||||
<div className="invite__blocks">
|
||||
<div className="addPersonBlock">
|
||||
@ -387,7 +468,7 @@ export const TrackerModal = ({
|
||||
<div className="input-container invitePersonBlock__input">
|
||||
<input
|
||||
className="name-project"
|
||||
placeholder="email"
|
||||
placeholder="e-mail"
|
||||
type="email"
|
||||
/>
|
||||
</div>
|
||||
@ -404,11 +485,22 @@ export const TrackerModal = ({
|
||||
<>
|
||||
<div className="title-project">
|
||||
<div className="createTaskHead">
|
||||
<span>Этап</span>
|
||||
<div className="createTaskHead__selectColumn">
|
||||
<span>Backlog</span>
|
||||
<img src={arrowCreateTask} alt="arrow" />
|
||||
<div className="createTaskBody__right__owner">
|
||||
<p>Создатель : {profileInfo?.fio}</p>
|
||||
<img
|
||||
src={
|
||||
profileInfo.photo
|
||||
? urlForLocal(profileInfo.photo)
|
||||
: avatarMok
|
||||
}
|
||||
alt="avatar"
|
||||
/>
|
||||
</div>
|
||||
{/*<span>Этап</span>*/}
|
||||
{/*<div className="createTaskHead__selectColumn">*/}
|
||||
{/* <span>Backlog</span>*/}
|
||||
{/* <img src={arrowCreateTask} alt="arrow" />*/}
|
||||
{/*</div>*/}
|
||||
</div>
|
||||
<div className="createTaskBody">
|
||||
<div className="createTaskBody__left">
|
||||
@ -445,16 +537,106 @@ export const TrackerModal = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="createTaskBody__right">
|
||||
<div className="createTaskBody__right__owner">
|
||||
<p>Создатель : {profileInfo?.fio}</p>
|
||||
<img
|
||||
src={
|
||||
profileInfo.photo
|
||||
? urlForLocal(profileInfo.photo)
|
||||
: avatarMok
|
||||
}
|
||||
alt="avatar"
|
||||
/>
|
||||
<div className="createTaskBody__right__tags">
|
||||
<div className="tags__selected">
|
||||
<div className="tags__selected__items">
|
||||
{taskTags.map((tag) => {
|
||||
return (
|
||||
<div
|
||||
className="selectedTag"
|
||||
key={tag.id}
|
||||
style={{ background: tag.color }}
|
||||
>
|
||||
<p>{tag.slug}</p>
|
||||
<img
|
||||
src={crossWhite}
|
||||
className="delete"
|
||||
alt="delete"
|
||||
onClick={() =>
|
||||
setTaskTags((prevState) =>
|
||||
prevState.filter(
|
||||
(prevTag) => prevTag.id !== tag.id
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div
|
||||
className="tags__selected__name"
|
||||
onClick={() => setSelectTagsOpen(!selectTagsOpen)}
|
||||
>
|
||||
Выберите тег
|
||||
<img
|
||||
className={
|
||||
selectTagsOpen ? "arrow arrow--open" : "arrow"
|
||||
}
|
||||
src={arrowDown}
|
||||
alt="arrow"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{selectTagsOpen && (
|
||||
<div className="tags__dropDown">
|
||||
<img
|
||||
src={close}
|
||||
className="close"
|
||||
onClick={() => setSelectTagsOpen(false)}
|
||||
/>
|
||||
{correctProjectTags.map((tag) => {
|
||||
return (
|
||||
<div
|
||||
className="tag__item"
|
||||
key={tag.id}
|
||||
onClick={() =>
|
||||
setTaskTags((prevState) => [...prevState, tag])
|
||||
}
|
||||
>
|
||||
<p>{tag.slug}</p>
|
||||
<span style={{ background: tag.color }} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{Boolean(!correctProjectTags.length) && (
|
||||
<p className="noTags">Нет тегов</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="select__priority">
|
||||
<div
|
||||
className="select__priority__name"
|
||||
onClick={() => setSelectPriority(!selectPriority)}
|
||||
>
|
||||
{selectedPriority
|
||||
? `Приоритет: ${selectedPriority.name}`
|
||||
: "Выберите приоритет"}
|
||||
<img
|
||||
className={selectPriority ? "arrow arrow--open" : "arrow"}
|
||||
src={arrowDown}
|
||||
alt="arrow"
|
||||
/>
|
||||
</div>
|
||||
{selectPriority && (
|
||||
<div className="select__priority__dropDown">
|
||||
{priority.map((item) => {
|
||||
return (
|
||||
<div
|
||||
className="dropdown__item"
|
||||
key={item.key}
|
||||
onClick={() => {
|
||||
setSelectPriority(false);
|
||||
setSelectedPriority(item);
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
onClick={() =>
|
||||
|
@ -162,7 +162,7 @@
|
||||
|
||||
&__dropDown {
|
||||
position: absolute;
|
||||
background: #F1F1F1;
|
||||
background: #f1f1f1;
|
||||
border-radius: 8px;
|
||||
top: 55px;
|
||||
left: 0;
|
||||
@ -233,6 +233,7 @@
|
||||
text-overflow: ellipsis;
|
||||
font-size: 14px;
|
||||
max-width: 270px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
img {
|
||||
@ -242,6 +243,8 @@
|
||||
}
|
||||
|
||||
&__dropDown {
|
||||
max-height: 155px;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
@ -263,6 +266,21 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #cbd9f9;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #c5c0c6;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
@ -282,7 +300,7 @@
|
||||
display: flex;
|
||||
column-gap: 9.5px;
|
||||
align-items: center;
|
||||
background: #F1F1F1;
|
||||
background: #f1f1f1;
|
||||
border-radius: 22px 22px 0 0;
|
||||
|
||||
span {
|
||||
@ -297,10 +315,10 @@
|
||||
|
||||
span {
|
||||
padding: 3.75px 16.25px 5.75px;
|
||||
color: #FFFFFF;
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
background: #52B709;
|
||||
background: #52b709;
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
@ -322,13 +340,13 @@
|
||||
}
|
||||
|
||||
.input-container {
|
||||
background: #F1F1F1;
|
||||
background: #f1f1f1;
|
||||
margin: 0 0 17px;
|
||||
width: 393px;
|
||||
height: 47px;
|
||||
|
||||
input {
|
||||
background: #F1F1F1;
|
||||
background: #f1f1f1;
|
||||
color: #000000;
|
||||
font-size: 15px;
|
||||
line-height: 18px;
|
||||
@ -337,7 +355,7 @@
|
||||
}
|
||||
|
||||
.ck-editor {
|
||||
border: 1px solid #DDDDDD;
|
||||
border: 1px solid #dddddd;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
@ -357,9 +375,8 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 9.5px;
|
||||
margin-bottom: 30px;
|
||||
p {
|
||||
color: #2D4A17;
|
||||
color: #2d4a17;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 32px;
|
||||
@ -371,14 +388,198 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__tags {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
|
||||
.tags {
|
||||
&__selected {
|
||||
width: 393px;
|
||||
font-weight: 300;
|
||||
line-height: 18px;
|
||||
font-size: 15px;
|
||||
margin-bottom: 17.5px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 8px;
|
||||
|
||||
&__name {
|
||||
color: #000;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 9.5px 12px;
|
||||
border-radius: 8px;
|
||||
height: 47px;
|
||||
border: 1px solid #e4e4e4;
|
||||
|
||||
img {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.arrow--open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
&__items {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
max-width: 393px;
|
||||
|
||||
.selectedTag {
|
||||
display: flex;
|
||||
padding: 7px 7px 7px 8px;
|
||||
border-radius: 35px;
|
||||
align-items: center;
|
||||
column-gap: 8px;
|
||||
|
||||
p {
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 0px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.delete {
|
||||
cursor: pointer;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__dropDown {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e4e4e4;
|
||||
top: 90%;
|
||||
z-index: 101;
|
||||
padding: 30px 10px 10px;
|
||||
background: #ebebeb;
|
||||
row-gap: 8px;
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
right: 9px;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.tag__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 42px;
|
||||
background: white;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
column-gap: 8px;
|
||||
padding: 9px 14px;
|
||||
border: 1px solid #ececec;
|
||||
border-radius: 8px;
|
||||
justify-content: space-between;
|
||||
|
||||
p {
|
||||
color: #000;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
line-height: 0px;
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
span {
|
||||
width: 22px;
|
||||
height: 23px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.noTags {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select__priority {
|
||||
position: relative;
|
||||
|
||||
&__name {
|
||||
color: #000;
|
||||
width: 393px;
|
||||
height: 47px;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 9.5px 12px;
|
||||
border-radius: 8px;
|
||||
height: 47px;
|
||||
border: 1px solid #e4e4e4;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
&__dropDown {
|
||||
position: absolute;
|
||||
border-radius: 8px;
|
||||
padding: 9.5px 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
width: 100%;
|
||||
background: #f1f1f1;
|
||||
z-index: 101;
|
||||
|
||||
.dropdown__item {
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.arrow--open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.select__executor {
|
||||
background: #F1F1F1;
|
||||
background: #f1f1f1;
|
||||
width: 393px;
|
||||
height: 47px;
|
||||
font-weight: 300;
|
||||
line-height: 18px;
|
||||
font-size: 15px;
|
||||
margin-bottom: 17.5px;
|
||||
margin-bottom: 27px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
@ -386,7 +587,7 @@
|
||||
margin: 0;
|
||||
width: 185px;
|
||||
height: 42px;
|
||||
color: #FFFFFF;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
@ -402,7 +603,7 @@
|
||||
margin-bottom: 22px;
|
||||
|
||||
span {
|
||||
color: #6F6F6F;
|
||||
color: #6f6f6f;
|
||||
}
|
||||
|
||||
p {
|
||||
@ -541,7 +742,7 @@
|
||||
.exit {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 36px;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
|
@ -26,7 +26,7 @@ export const Navigation = () => {
|
||||
},
|
||||
{
|
||||
path: "/calendar",
|
||||
name: "Отчетность",
|
||||
name: "Отчеты",
|
||||
},
|
||||
{
|
||||
path: "/tracker",
|
||||
@ -73,8 +73,15 @@ export const Navigation = () => {
|
||||
if (localStorage.getItem("role_status") === "18") {
|
||||
return;
|
||||
}
|
||||
apiRequest(`/profile/${localStorage.getItem("cardId")}`).then(
|
||||
(profileInfo) => dispatch(setProfileInfo(profileInfo))
|
||||
if (Object.keys(profileInfo).length) {
|
||||
return;
|
||||
}
|
||||
apiRequest(`/user/me`).then((profileInfo) =>
|
||||
dispatch(
|
||||
setProfileInfo(
|
||||
profileInfo.userCard ? profileInfo.userCard : profileInfo
|
||||
)
|
||||
)
|
||||
);
|
||||
}, [dispatch]);
|
||||
|
||||
@ -93,12 +100,12 @@ export const Navigation = () => {
|
||||
|
||||
<div className="profileHeader__personalInfo">
|
||||
<h3 className="profileHeader__personalInfoName">
|
||||
{user === "developer" ? profileInfo?.fio : ""}
|
||||
{profileInfo?.fio ? profileInfo?.fio : profileInfo?.username}
|
||||
</h3>
|
||||
<NavLink end to={"/profile"}>
|
||||
<img
|
||||
src={
|
||||
profileInfo.photo ? urlForLocal(profileInfo.photo) : avatarMok
|
||||
profileInfo?.photo ? urlForLocal(profileInfo.photo) : avatarMok
|
||||
}
|
||||
className="profileHeader__personalInfoAvatar"
|
||||
alt="avatar"
|
||||
|
@ -1,7 +1,7 @@
|
||||
.notification {
|
||||
border-radius: 40px;
|
||||
background: linear-gradient(180deg, #FFF 0%, #EBEBEB 100%);
|
||||
padding: 20px 82px 17px 27px;
|
||||
padding: 15px 60px 15px 15px;
|
||||
position: fixed;
|
||||
bottom: 25px;
|
||||
right: 25px;
|
||||
@ -13,7 +13,7 @@
|
||||
align-items: center;
|
||||
|
||||
h2 {
|
||||
max-width: 194px;
|
||||
max-width: 210px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
margin-bottom: 0;
|
||||
@ -27,8 +27,8 @@
|
||||
&__close {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 25px;
|
||||
top: 10px;
|
||||
right: 20px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
a {
|
||||
color: #5b6871;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
line-height: 16px;
|
||||
transition: 0.3s all ease;
|
||||
position: relative;
|
||||
@ -17,14 +17,6 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@media (max-width: 525px) {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 455px) {
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: #000000;
|
||||
|
@ -22,6 +22,8 @@ import { Navigation } from "@components/Navigation/Navigation";
|
||||
import { ProfileBreadcrumbs } from "@components/ProfileBreadcrumbs/ProfileBreadcrumbs";
|
||||
import { ProfileHeader } from "@components/ProfileHeader/ProfileHeader";
|
||||
|
||||
import avatarMok from "assets/images/avatarMok.png";
|
||||
|
||||
import { ProfileCalendarComponent } from "./ProfileCalendarComponent";
|
||||
import "./profileCalendar.scss";
|
||||
|
||||
@ -87,29 +89,30 @@ export const ProfileCalendar = () => {
|
||||
<ProfileBreadcrumbs
|
||||
links={[
|
||||
{ name: "Главная", link: "/profile" },
|
||||
{ name: "Ваша отчетность", link: "/profile/calendar" },
|
||||
{ name: "Отчеты", link: "/profile/calendar" },
|
||||
]}
|
||||
/>
|
||||
<h2 className="summary__title">Ваши отчеты</h2>
|
||||
<div className="summary__info">
|
||||
<div className="summary__person">
|
||||
<img
|
||||
src={urlForLocal(profileInfo.photo)}
|
||||
src={
|
||||
profileInfo?.photo ? urlForLocal(profileInfo.photo) : avatarMok
|
||||
}
|
||||
className="summary__avatar"
|
||||
alt="avatar"
|
||||
/>
|
||||
<p className="summary__name">
|
||||
{profileInfo.fio}, {profileInfo.specification} разработчик
|
||||
{profileInfo?.fio ? profileInfo?.fio : profileInfo?.username},{" "}
|
||||
{profileInfo.specification} разработчик
|
||||
</p>
|
||||
</div>
|
||||
<Link to="/report">
|
||||
<button
|
||||
className="calendar__btn"
|
||||
onClick={() => {
|
||||
dispatch(setReportDate(""));
|
||||
}}
|
||||
onClick={() => dispatch(setReportDate(""))}
|
||||
>
|
||||
Заполнить отчет за день
|
||||
Заполнить отчет
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
|
@ -205,11 +205,11 @@ export const ProfileCalendarComponent = React.memo(
|
||||
<div className="calendar-component">
|
||||
<div className="calendar-component__header">
|
||||
<div className="calendar-component__header-info">
|
||||
<h3>Мои отчеты:</h3>
|
||||
<h3>Мои отчеты за </h3>
|
||||
<p className="calendar__hours">
|
||||
{month}
|
||||
<span>
|
||||
{totalHours} {hourOfNum(totalHours)}{" "}
|
||||
({totalHours} {hourOfNum(totalHours)})
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@ -316,19 +316,23 @@ export const ProfileCalendarComponent = React.memo(
|
||||
? `${getCorrectDate(startDate)} - ${getCorrectDate(endDate)}`
|
||||
: `${getCorrectDate(endDate)} - ${getCorrectDate(startDate)}`
|
||||
: activePeriod
|
||||
? "Выберите начало диапазона"
|
||||
? "Выберите диапазон на календаре"
|
||||
: "Выбрать диапазон"}
|
||||
</span>
|
||||
<span>
|
||||
{totalRangeHours
|
||||
? `${totalRangeHours} ${hourOfNum(totalRangeHours)}`
|
||||
: "0 часов"}
|
||||
: endDate
|
||||
? "0 часов"
|
||||
: ""}
|
||||
</span>
|
||||
{endDate && (
|
||||
<BaseButton
|
||||
styles={"clear-days"}
|
||||
onClick={() => {
|
||||
resetRangeDays();
|
||||
toggleActivePeriod();
|
||||
toggleRangeDays();
|
||||
}}
|
||||
>
|
||||
Сбросить
|
||||
|
@ -78,7 +78,6 @@
|
||||
margin: 0;
|
||||
max-width: 220px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,7 @@ import React, { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { NavLink, useNavigate } from "react-router-dom";
|
||||
|
||||
import { auth, setProfileInfo } from "@redux/outstaffingSlice";
|
||||
import { getRole } from "@redux/roleSlice";
|
||||
import { auth, getProfileInfo, setProfileInfo } from "@redux/outstaffingSlice";
|
||||
|
||||
import { apiRequest } from "@api/request";
|
||||
|
||||
@ -14,8 +13,7 @@ import "./profileHeader.scss";
|
||||
export const ProfileHeader = () => {
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const userRole = useSelector(getRole);
|
||||
const profileInfo = useSelector(getProfileInfo);
|
||||
const [user] = useState(
|
||||
localStorage.getItem("role_status") === "18" ? "partner" : "developer"
|
||||
);
|
||||
@ -26,9 +24,16 @@ export const ProfileHeader = () => {
|
||||
if (localStorage.getItem("role_status") === "18") {
|
||||
return;
|
||||
}
|
||||
apiRequest(`/profile/${localStorage.getItem("cardId")}`).then(
|
||||
(profileInfo) => dispatch(setProfileInfo(profileInfo))
|
||||
);
|
||||
if (Object.keys(profileInfo).length) {
|
||||
return;
|
||||
}
|
||||
apiRequest(`/user/me`).then((profileInfo) => {
|
||||
dispatch(
|
||||
setProfileInfo(
|
||||
profileInfo.userCard ? profileInfo.userCard : profileInfo
|
||||
)
|
||||
);
|
||||
});
|
||||
}, [dispatch]);
|
||||
|
||||
const handler = () => {
|
||||
@ -36,7 +41,7 @@ export const ProfileHeader = () => {
|
||||
localStorage.clear();
|
||||
dispatch(auth(false));
|
||||
setIsLoggingOut(false);
|
||||
navigate(userRole === "ROLE_DEV" ? "/authdev" : "/auth");
|
||||
navigate("/auth");
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -25,6 +25,7 @@ import "./projectTiket.scss";
|
||||
export const ProjectTiket = ({ project, index }) => {
|
||||
const [modalSelect, setModalSelect] = useState(false);
|
||||
const [modalAdd, setModalAdd] = useState(false);
|
||||
const [modalDelete, setModalDelete] = useState(false);
|
||||
const [acceptModalOpen, setAcceptModalOpen] = useState(false);
|
||||
const [path, setPath] = useState("");
|
||||
const dispatch = useDispatch();
|
||||
@ -43,7 +44,10 @@ export const ProjectTiket = ({ project, index }) => {
|
||||
|
||||
if (
|
||||
event &&
|
||||
!path.find((item) => item.classList && item.classList.contains("project"))
|
||||
!path.find(
|
||||
(div) =>
|
||||
div.classList && div.classList.contains(`project-${project.id}`)
|
||||
)
|
||||
) {
|
||||
setModalSelect(false);
|
||||
}
|
||||
@ -68,14 +72,18 @@ export const ProjectTiket = ({ project, index }) => {
|
||||
|
||||
function closeAcceptModal() {
|
||||
setAcceptModalOpen(false);
|
||||
setModalDelete(false);
|
||||
}
|
||||
|
||||
function linkProject() {}
|
||||
|
||||
return (
|
||||
<div className="project" key={index}>
|
||||
<Link to={`/tracker/project/${project.id}`}>
|
||||
<p className="project__link">{project.name}</p>
|
||||
<div className={`project project-${project.id}`} key={index}>
|
||||
<Link
|
||||
to={`/tracker/project/${project.id}`}
|
||||
className="project__open-traker"
|
||||
>
|
||||
<div className="project__link">{project.name}</div>
|
||||
|
||||
<div className="project__info">
|
||||
<p>Открытые задачи</p>
|
||||
@ -90,9 +98,15 @@ export const ProjectTiket = ({ project, index }) => {
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<span className="menu-settings" onClick={() => setModalSelect(true)}>
|
||||
<span
|
||||
className="menu-settings"
|
||||
onClick={() => {
|
||||
setModalSelect(!modalSelect);
|
||||
}}
|
||||
>
|
||||
...
|
||||
</span>
|
||||
|
||||
<Link
|
||||
to={`/profile/statistics/${project.id}`}
|
||||
className="project__statistics"
|
||||
@ -132,7 +146,13 @@ export const ProjectTiket = ({ project, index }) => {
|
||||
<img src={archiveSet}></img>
|
||||
<p>в архив</p>
|
||||
</div>
|
||||
<div onClick={removeProject}>
|
||||
|
||||
<div
|
||||
onClick={() => {
|
||||
setModalDelete(true);
|
||||
setModalSelect(false);
|
||||
}}
|
||||
>
|
||||
<img src={del}></img>
|
||||
<p>удалить</p>
|
||||
</div>
|
||||
@ -140,6 +160,15 @@ export const ProjectTiket = ({ project, index }) => {
|
||||
</ModalSelect>
|
||||
{acceptModalOpen && (
|
||||
<AcceptModal
|
||||
title={"Вы точно хотите переместить проект в архив?"}
|
||||
closeModal={closeAcceptModal}
|
||||
agreeHandler={removeProject}
|
||||
/>
|
||||
)}
|
||||
|
||||
{modalDelete && (
|
||||
<AcceptModal
|
||||
title={"Вы точно хотите удалить?"}
|
||||
closeModal={closeAcceptModal}
|
||||
agreeHandler={removeProject}
|
||||
/>
|
||||
|
@ -1,10 +1,12 @@
|
||||
.project {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
width: 322px;
|
||||
|
||||
background: #f1f1f1;
|
||||
border-radius: 12px;
|
||||
padding: 17px 26px 16px;
|
||||
|
||||
cursor: pointer;
|
||||
max-width: 440px;
|
||||
transition: 0.4s;
|
||||
@ -24,14 +26,14 @@
|
||||
|
||||
&__link {
|
||||
font-weight: 700;
|
||||
width: 194px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 18px;
|
||||
line-height: 32px;
|
||||
color: #111112;
|
||||
margin-bottom: 22px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 380px;
|
||||
|
||||
&:hover {
|
||||
@ -43,7 +45,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
margin-bottom: 27px;
|
||||
margin-bottom: 45px;
|
||||
|
||||
p {
|
||||
color: #6f6f6f;
|
||||
@ -85,7 +87,7 @@
|
||||
font-size: 21px;
|
||||
color: #6f6f6f;
|
||||
right: 26px;
|
||||
top: 59px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
@ -94,12 +96,18 @@
|
||||
margin-left: 56px;
|
||||
}
|
||||
|
||||
&__open-traker {
|
||||
padding: 17px 26px 16px;
|
||||
}
|
||||
|
||||
&__statistics {
|
||||
color: #678eda;
|
||||
margin-top: 27px;
|
||||
position: absolute;
|
||||
bottom: 18px;
|
||||
left: 26px;
|
||||
color: #0042b4;
|
||||
text-decoration: underline;
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 17px;
|
||||
}
|
||||
}
|
||||
|
@ -125,8 +125,8 @@ const ReportForm = () => {
|
||||
<ProfileBreadcrumbs
|
||||
links={[
|
||||
{ name: "Главная", link: "/profile" },
|
||||
{ name: "Ваша отчетность", link: "/profile/calendar" },
|
||||
{ name: "Страница добавления нового отчета", link: "/report" },
|
||||
{ name: "Отчеты", link: "/profile/calendar" },
|
||||
{ name: "Добавить отчет", link: "/report" },
|
||||
]}
|
||||
/>
|
||||
<h2 className="summary__title">
|
||||
@ -180,9 +180,7 @@ const ReportForm = () => {
|
||||
<p className="report-form__task-title--description">
|
||||
Краткое описание задачи
|
||||
</p>
|
||||
<p className="report-form__task-title--hours">
|
||||
Количество часов
|
||||
</p>
|
||||
<p className="report-form__task-title--hours">Кол-во часов</p>
|
||||
</div>
|
||||
|
||||
{inputs.map((input, index) => {
|
||||
@ -260,7 +258,7 @@ const ReportForm = () => {
|
||||
<p className="addMore" onClick={addInput}>
|
||||
+
|
||||
</p>
|
||||
<span>Добавить еще </span>
|
||||
<span>Добавить задачу</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -299,7 +297,7 @@ const ReportForm = () => {
|
||||
{isFetching ? <Loader /> : "Отправить"}
|
||||
</button>
|
||||
<p className="report-form__footer-text">
|
||||
Всего за день :{" "}
|
||||
Всего за день:{" "}
|
||||
<span>
|
||||
{totalHours} {hourOfNum(totalHours)}
|
||||
</span>
|
||||
|
@ -54,7 +54,6 @@
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
letter-spacing: normal;
|
||||
line-height: 48.74px;
|
||||
text-align: left;
|
||||
|
||||
@media (max-width: 555px) {
|
||||
@ -75,7 +74,7 @@
|
||||
|
||||
@media (max-width: 555px) {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 15px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -115,12 +114,11 @@
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
letter-spacing: normal;
|
||||
line-height: 48.74px;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
&-list {
|
||||
margin-top: 40px;
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@ -146,7 +144,7 @@
|
||||
&-header {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
margin-top: 40px;
|
||||
margin-top: 20px;
|
||||
margin-left: 50px;
|
||||
|
||||
p {
|
||||
@ -156,7 +154,7 @@
|
||||
letter-spacing: normal;
|
||||
line-height: normal;
|
||||
text-align: left;
|
||||
margin-bottom: 26px;
|
||||
margin-bottom: 10px;
|
||||
white-space: nowrap;
|
||||
|
||||
@media (max-width: 450px) {
|
||||
@ -276,6 +274,7 @@
|
||||
margin-left: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.addMore {
|
||||
display: flex;
|
||||
@ -285,9 +284,9 @@
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
background: #e8e8e8;
|
||||
margin-bottom: 0;
|
||||
border-radius: 50px;
|
||||
font-size: 32px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
span {
|
||||
@ -297,7 +296,8 @@
|
||||
letter-spacing: normal;
|
||||
line-height: normal;
|
||||
text-align: left;
|
||||
margin-left: 20px;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@ -352,6 +352,7 @@
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
|
||||
@ -380,16 +381,13 @@
|
||||
&-text {
|
||||
font-family: "GT Eesti Pro Display";
|
||||
font-size: 1.9em;
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
letter-spacing: normal;
|
||||
line-height: 22.38px;
|
||||
text-align: left;
|
||||
margin-left: 40px;
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 20px;
|
||||
|
||||
span {
|
||||
font-weight: 100;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import React, { useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import arrow from "assets/icons/sideBarArrow.svg";
|
||||
import LogoITguild from "assets/images/logo/LogoITguild.svg";
|
||||
import ITguild from "assets/images/logo/ITguild.svg";
|
||||
|
||||
import "./sidebar.scss";
|
||||
|
||||
@ -37,13 +37,13 @@ export const SideBar = () => {
|
||||
</div>
|
||||
<p className="outstaffing">
|
||||
<img src={arrow}></img>
|
||||
2023 © Outstaffing
|
||||
{new Date().getFullYear()} © Outstaffing
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={active ? "auth-body active" : "auth-body"}>
|
||||
<div className="auth-body__title">
|
||||
<img src={LogoITguild}></img>
|
||||
<img src={ITguild}></img>
|
||||
</div>
|
||||
<ul className="auth-body__navigation">
|
||||
<li>
|
||||
@ -72,10 +72,10 @@ export const SideBar = () => {
|
||||
</li>
|
||||
</ul>
|
||||
<p className="auth-body__politic">Политика конфиденциальности</p>
|
||||
<div className="auth-body__contacts">
|
||||
<h4>+7 812 363 17 87</h4>
|
||||
<p>Перезвонить Вам?</p>
|
||||
</div>
|
||||
{/*<div className="auth-body__contacts">*/}
|
||||
{/* <h4>+7 812 363 17 87</h4>*/}
|
||||
{/* <p>Перезвонить Вам?</p>*/}
|
||||
{/*</div>*/}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -125,6 +125,10 @@
|
||||
&__title {
|
||||
display: flex;
|
||||
margin-top: 24px;
|
||||
|
||||
img {
|
||||
width: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
&__navigation {
|
||||
@ -143,7 +147,7 @@
|
||||
|
||||
&__politic {
|
||||
margin-top: 42px;
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: #000000;
|
||||
}
|
||||
@ -156,7 +160,7 @@
|
||||
line-height: 33px;
|
||||
}
|
||||
p {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,6 +194,6 @@
|
||||
@media (max-width: 1375px) {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 605px;
|
||||
height: 705px;
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ export const SliderWorkers = ({ title, titleInfo, subTitle }) => {
|
||||
</Slider>
|
||||
{Boolean(subTitle) ? (
|
||||
<div className="slider-workers__description">
|
||||
<h2>Дополните свою команду опытными ИТ-специалистами</h2>
|
||||
<h2>Дополните Вашу команду опытными IT-специалистами</h2>
|
||||
<p>
|
||||
Даём финансовые, юридические и кадровые гарантии, предоставляем
|
||||
SLA и отвечаем за работу команды. Вам не нужно искать, оформлять
|
||||
|
@ -46,11 +46,14 @@
|
||||
padding: 8px 26px;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
|
||||
@media (max-width: 450px) {
|
||||
max-width: 180px;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 55px;
|
||||
margin-left: 20px;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
|
@ -47,7 +47,7 @@ export const Instruction = () => {
|
||||
<img src={comment} alt="" />
|
||||
</div>
|
||||
<div className="instruction__text instruction__text_info">
|
||||
Количество вопросов в тесте: <span>{countQuestions}</span>
|
||||
Вопросов в тесте: <span>{countQuestions}</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
Reference in New Issue
Block a user