Fixed full components
@ -1,103 +0,0 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
|
||||||
import { NavLink } from 'react-router-dom'
|
|
||||||
import { urlForLocal } from '../../helper'
|
|
||||||
import { apiRequest } from '../../api/request';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
import { getProfileInfo, setProfileInfo } from '../../redux/outstaffingSlice';
|
|
||||||
|
|
||||||
import avatarMok from "../../pages/PartnerTreaties/Images/avatarMok.png"
|
|
||||||
|
|
||||||
export const Navigation = () => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
|
|
||||||
const profileInfo = useSelector(getProfileInfo);
|
|
||||||
const [user] = useState(localStorage.getItem('role_status') === '18' ? 'partner' : 'developer')
|
|
||||||
const [navInfo] = useState({
|
|
||||||
developer: [
|
|
||||||
{
|
|
||||||
path: '/summary',
|
|
||||||
name: 'Резюме'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/calendar',
|
|
||||||
name: 'Отчетность'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/tracker',
|
|
||||||
name: 'Трекер'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/payouts',
|
|
||||||
name: 'Выплаты'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/settings',
|
|
||||||
name: 'Настройки'
|
|
||||||
},
|
|
||||||
],
|
|
||||||
partner: [
|
|
||||||
{
|
|
||||||
path: '/catalog',
|
|
||||||
name: 'Каталог'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/requests',
|
|
||||||
name: 'Запросы'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/categories',
|
|
||||||
name: 'Персонал'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/tracker',
|
|
||||||
name: 'Трекер'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/treaties',
|
|
||||||
name: 'Договора'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/settings',
|
|
||||||
name: 'Настройки'
|
|
||||||
},
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (localStorage.getItem('role_status') === '18') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
apiRequest(`/profile/${localStorage.getItem('cardId')}`)
|
|
||||||
.then((profileInfo) =>
|
|
||||||
dispatch(setProfileInfo(profileInfo))
|
|
||||||
);
|
|
||||||
|
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='profileHeader__info'>
|
|
||||||
<div className='profileHeader__container'>
|
|
||||||
<nav className='profileHeader__nav'>
|
|
||||||
{
|
|
||||||
navInfo[user].map((link, index) => {
|
|
||||||
return <NavLink key={index} end to={`/profile${link.path}`}>{link.name}</NavLink>
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div className='profileHeader__personalInfo'>
|
|
||||||
<h3 className='profileHeader__personalInfoName'>
|
|
||||||
{user === 'developer' ?
|
|
||||||
profileInfo?.fio :
|
|
||||||
''
|
|
||||||
}
|
|
||||||
</h3>
|
|
||||||
<NavLink end to={'/profile'}>
|
|
||||||
<img src={profileInfo.photo ? urlForLocal(profileInfo.photo) : avatarMok} className='profileHeader__personalInfoAvatar' alt='avatar' />
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
107
src/components/Navigation/Navigation.jsx
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { NavLink } from "react-router-dom";
|
||||||
|
import { urlForLocal } from "../../helper";
|
||||||
|
import { apiRequest } from "../../api/request";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { getProfileInfo, setProfileInfo } from "../../redux/outstaffingSlice";
|
||||||
|
|
||||||
|
import avatarMok from "../../pages/PartnerTreaties/Images/avatarMok.png";
|
||||||
|
|
||||||
|
export const Navigation = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const profileInfo = useSelector(getProfileInfo);
|
||||||
|
const [user] = useState(
|
||||||
|
localStorage.getItem("role_status") === "18" ? "partner" : "developer"
|
||||||
|
);
|
||||||
|
const [navInfo] = useState({
|
||||||
|
developer: [
|
||||||
|
{
|
||||||
|
path: "/summary",
|
||||||
|
name: "Резюме",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/calendar",
|
||||||
|
name: "Отчетность",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/tracker",
|
||||||
|
name: "Трекер",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/payouts",
|
||||||
|
name: "Выплаты",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/settings",
|
||||||
|
name: "Настройки",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
partner: [
|
||||||
|
{
|
||||||
|
path: "/catalog",
|
||||||
|
name: "Каталог",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/requests",
|
||||||
|
name: "Запросы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/categories",
|
||||||
|
name: "Персонал",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/tracker",
|
||||||
|
name: "Трекер",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/treaties",
|
||||||
|
name: "Договора",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/settings",
|
||||||
|
name: "Настройки",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (localStorage.getItem("role_status") === "18") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
apiRequest(`/profile/${localStorage.getItem("cardId")}`).then(
|
||||||
|
(profileInfo) => dispatch(setProfileInfo(profileInfo))
|
||||||
|
);
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="profileHeader__info">
|
||||||
|
<div className="profileHeader__container">
|
||||||
|
<nav className="profileHeader__nav">
|
||||||
|
{navInfo[user].map((link, index) => {
|
||||||
|
return (
|
||||||
|
<NavLink key={index} end to={`/profile${link.path}`}>
|
||||||
|
{link.name}
|
||||||
|
</NavLink>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div className="profileHeader__personalInfo">
|
||||||
|
<h3 className="profileHeader__personalInfoName">
|
||||||
|
{user === "developer" ? profileInfo?.fio : ""}
|
||||||
|
</h3>
|
||||||
|
<NavLink end to={"/profile"}>
|
||||||
|
<img
|
||||||
|
src={
|
||||||
|
profileInfo.photo ? urlForLocal(profileInfo.photo) : avatarMok
|
||||||
|
}
|
||||||
|
className="profileHeader__personalInfoAvatar"
|
||||||
|
alt="avatar"
|
||||||
|
/>
|
||||||
|
</NavLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -1,61 +0,0 @@
|
|||||||
import React, {useEffect, useState} from 'react';
|
|
||||||
import {useNavigate, NavLink} from "react-router-dom";
|
|
||||||
import {useDispatch, useSelector} from "react-redux";
|
|
||||||
|
|
||||||
import {Loader} from '../Loader/Loader'
|
|
||||||
import {apiRequest} from "../../api/request";
|
|
||||||
import {auth, setProfileInfo} from "../../redux/outstaffingSlice";
|
|
||||||
import {getRole} from "../../redux/roleSlice";
|
|
||||||
import './profileHeader.scss'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const ProfileHeader = () => {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
|
|
||||||
const userRole = useSelector(getRole);
|
|
||||||
const [user] = useState(localStorage.getItem('role_status') === '18' ? 'partner' : 'developer')
|
|
||||||
|
|
||||||
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (localStorage.getItem('role_status') === '18') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
apiRequest(`/profile/${localStorage.getItem('cardId')}`)
|
|
||||||
.then((profileInfo) =>
|
|
||||||
dispatch(setProfileInfo(profileInfo))
|
|
||||||
);
|
|
||||||
|
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
const handler = () => {
|
|
||||||
setIsLoggingOut(true);
|
|
||||||
localStorage.clear();
|
|
||||||
dispatch(auth(false));
|
|
||||||
setIsLoggingOut(false);
|
|
||||||
navigate(userRole === 'ROLE_DEV' ? '/authdev' : '/auth')
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<header className='profileHeader'>
|
|
||||||
<div className='profileHeader__head'>
|
|
||||||
<div className='profileHeader__container'>
|
|
||||||
<NavLink to={'/profile'} className='profileHeader__title'>itguild.
|
|
||||||
<span>
|
|
||||||
{user === 'developer' ?
|
|
||||||
'для разработчиков' :
|
|
||||||
'для партнеров'
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
</NavLink>
|
|
||||||
<button onClick={handler} className='profileHeader__logout'>
|
|
||||||
{isLoggingOut ? <Loader/> : 'Выйти'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
)
|
|
||||||
};
|
|
57
src/components/ProfileHeader/ProfileHeader.jsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useNavigate, NavLink } from "react-router-dom";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
|
||||||
|
import { Loader } from "../Loader/Loader";
|
||||||
|
import { apiRequest } from "../../api/request";
|
||||||
|
import { auth, setProfileInfo } from "../../redux/outstaffingSlice";
|
||||||
|
import { getRole } from "../../redux/roleSlice";
|
||||||
|
|
||||||
|
import "./profileHeader.scss";
|
||||||
|
|
||||||
|
export const ProfileHeader = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const userRole = useSelector(getRole);
|
||||||
|
const [user] = useState(
|
||||||
|
localStorage.getItem("role_status") === "18" ? "partner" : "developer"
|
||||||
|
);
|
||||||
|
|
||||||
|
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (localStorage.getItem("role_status") === "18") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
apiRequest(`/profile/${localStorage.getItem("cardId")}`).then(
|
||||||
|
(profileInfo) => dispatch(setProfileInfo(profileInfo))
|
||||||
|
);
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const handler = () => {
|
||||||
|
setIsLoggingOut(true);
|
||||||
|
localStorage.clear();
|
||||||
|
dispatch(auth(false));
|
||||||
|
setIsLoggingOut(false);
|
||||||
|
navigate(userRole === "ROLE_DEV" ? "/authdev" : "/auth");
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header className="profileHeader">
|
||||||
|
<div className="profileHeader__head">
|
||||||
|
<div className="profileHeader__container">
|
||||||
|
<NavLink to={"/profile"} className="profileHeader__title">
|
||||||
|
itguild.
|
||||||
|
<span>
|
||||||
|
{user === "developer" ? "для разработчиков" : "для партнеров"}
|
||||||
|
</span>
|
||||||
|
</NavLink>
|
||||||
|
<button onClick={handler} className="profileHeader__logout">
|
||||||
|
{isLoggingOut ? <Loader /> : "Выйти"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
};
|
@ -2,10 +2,10 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
font-family: 'LabGrotesque', sans-serif;
|
font-family: "LabGrotesque", sans-serif;
|
||||||
|
|
||||||
&__head {
|
&__head {
|
||||||
background: #E1FCCF;
|
background: #e1fccf;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__container {
|
&__container {
|
||||||
@ -26,7 +26,7 @@
|
|||||||
color: black;
|
color: black;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
color: #52B709;
|
color: #52b709;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@ -45,7 +45,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__info {
|
&__info {
|
||||||
background: #FFFFFF;
|
background: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__nav {
|
&__nav {
|
||||||
|
@ -13,20 +13,19 @@ registerLocale("ru", ru);
|
|||||||
import { Loader } from "../Loader/Loader";
|
import { Loader } from "../Loader/Loader";
|
||||||
import { Footer } from "../Footer/Footer";
|
import { Footer } from "../Footer/Footer";
|
||||||
import { ProfileHeader } from "../ProfileHeader/ProfileHeader";
|
import { ProfileHeader } from "../ProfileHeader/ProfileHeader";
|
||||||
import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs";
|
import { ProfileBreadcrumbs } from "../ProfileBreadcrumbs/ProfileBreadcrumbs";
|
||||||
|
|
||||||
import { apiRequest } from "../../api/request";
|
import { apiRequest } from "../../api/request";
|
||||||
|
import { Navigation } from "../Navigation/Navigation";
|
||||||
import { getReportDate } from "../../redux/reportSlice";
|
import { getReportDate } from "../../redux/reportSlice";
|
||||||
|
|
||||||
import calendarIcon from "../../images/calendar.svg";
|
import calendarIcon from "../../images/calendar.svg";
|
||||||
import ellipse from "../../images/ellipse.png";
|
import ellipse from "../../images/ellipse.png";
|
||||||
import remove from "../../images/remove.png";
|
import remove from "../../images/remove.svg";
|
||||||
import arrow from "../../images/right-arrow.png";
|
import arrow from "../../images/left-arrow.png";
|
||||||
|
|
||||||
import "./reportForm.scss";
|
import "./reportForm.scss";
|
||||||
import "react-datepicker/dist/react-datepicker.css";
|
import "react-datepicker/dist/react-datepicker.css";
|
||||||
import { Navigation } from "../Navigation/Navigation";
|
|
||||||
|
|
||||||
const ReportForm = () => {
|
const ReportForm = () => {
|
||||||
if (localStorage.getItem("role_status") === "18") {
|
if (localStorage.getItem("role_status") === "18") {
|
@ -1,86 +0,0 @@
|
|||||||
.search {
|
|
||||||
margin-top: 73px;
|
|
||||||
}
|
|
||||||
.search__title {
|
|
||||||
font-family: 'GT Eesti Pro Display', sans-serif;
|
|
||||||
font-size: 2.4em;
|
|
||||||
font-weight: 500;
|
|
||||||
font-style: normal;
|
|
||||||
letter-spacing: normal;
|
|
||||||
line-height: normal;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__box {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 575.98px) {
|
|
||||||
.search__box {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__box > button {
|
|
||||||
color: white;
|
|
||||||
width: 131px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: none;
|
|
||||||
font-family: 'Muller', sans-serif;
|
|
||||||
font-size: 1.6em;
|
|
||||||
letter-spacing: 0.8px;
|
|
||||||
text-align: center;
|
|
||||||
background-color: #73c141;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__box > button:hover {
|
|
||||||
background: rgba(0, 0, 0, 0);
|
|
||||||
color: #73c141;
|
|
||||||
box-shadow: inset 0 0 0 3px #73c141;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 575.98px) {
|
|
||||||
.search__box > button {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.select {
|
|
||||||
width: 85%;
|
|
||||||
}
|
|
||||||
|
|
||||||
[class$='-placeholder'] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
[class$='-control'] {
|
|
||||||
border-color: #e8e8e8 !important;
|
|
||||||
box-shadow: 0 0 0 0 #e8e8e8 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
[class$='-value__label'] {
|
|
||||||
font-size: 1.4em !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
[class$='-option'] {
|
|
||||||
font-size: 1.6em !important;
|
|
||||||
color: gray !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__submit {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__submit:hover .loader path {
|
|
||||||
fill: #6aaf5c;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 990px) {
|
|
||||||
.select {
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
import React, {useState} from 'react'
|
|
||||||
import {useSelector, useDispatch} from 'react-redux'
|
|
||||||
import Select from 'react-select'
|
|
||||||
import {Loader} from '../Loader/Loader'
|
|
||||||
import {apiRequest} from "../../api/request";
|
|
||||||
import {
|
|
||||||
selectedItems,
|
|
||||||
selectItems,
|
|
||||||
selectTags,
|
|
||||||
profiles,
|
|
||||||
setPositionId
|
|
||||||
} from '../../redux/outstaffingSlice'
|
|
||||||
|
|
||||||
import './TagSelect.css'
|
|
||||||
|
|
||||||
|
|
||||||
const TagSelect = () => {
|
|
||||||
|
|
||||||
const [searchLoading, setSearchLoading] = useState(false);
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
|
|
||||||
const itemsArr = useSelector(selectItems);
|
|
||||||
const tagsArr = useSelector(selectTags);
|
|
||||||
|
|
||||||
const handleSubmit = ({dispatch, setSearchLoading}) => {
|
|
||||||
setSearchLoading(true);
|
|
||||||
|
|
||||||
dispatch(setPositionId(null));
|
|
||||||
const filterItemsId = itemsArr.map((item) => item.id).join();
|
|
||||||
const params = filterItemsId ? {skill: filterItemsId} : '';
|
|
||||||
|
|
||||||
|
|
||||||
apiRequest('/profile', {
|
|
||||||
params: {...params, limit: 1000},
|
|
||||||
}).then((res) => {
|
|
||||||
dispatch(profiles(res));
|
|
||||||
setSearchLoading(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
// dispatch(selectedItems([]));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<section className='search'>
|
|
||||||
<div className='row'>
|
|
||||||
<div className='col-12'>
|
|
||||||
<h2 className='search__title'>
|
|
||||||
Найти специалиста по навыкам
|
|
||||||
</h2>
|
|
||||||
<div className='search__box'>
|
|
||||||
<Select
|
|
||||||
value={itemsArr}
|
|
||||||
onChange={(value) => dispatch(selectedItems(value))}
|
|
||||||
isMulti
|
|
||||||
name='tags'
|
|
||||||
className='select'
|
|
||||||
classNamePrefix='select'
|
|
||||||
options={
|
|
||||||
tagsArr &&
|
|
||||||
tagsArr.flat().map((item) => {
|
|
||||||
return {
|
|
||||||
id: item.id,
|
|
||||||
value: item.value,
|
|
||||||
label: item.value
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
onClick={() => handleSubmit({dispatch, setSearchLoading})}
|
|
||||||
type='submit'
|
|
||||||
className='search__submit'
|
|
||||||
>
|
|
||||||
{searchLoading ? <Loader width={30} height={30}/> : 'Поиск'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TagSelect
|
|
79
src/components/Select/TagSelect.jsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { useSelector, useDispatch } from "react-redux";
|
||||||
|
import Select from "react-select";
|
||||||
|
|
||||||
|
import { Loader } from "../Loader/Loader";
|
||||||
|
import { apiRequest } from "../../api/request";
|
||||||
|
import {
|
||||||
|
selectedItems,
|
||||||
|
selectItems,
|
||||||
|
selectTags,
|
||||||
|
profiles,
|
||||||
|
setPositionId,
|
||||||
|
} from "../../redux/outstaffingSlice";
|
||||||
|
|
||||||
|
import "./tagSelect.scss";
|
||||||
|
|
||||||
|
const TagSelect = () => {
|
||||||
|
const [searchLoading, setSearchLoading] = useState(false);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const itemsArr = useSelector(selectItems);
|
||||||
|
const tagsArr = useSelector(selectTags);
|
||||||
|
|
||||||
|
const handleSubmit = ({ dispatch, setSearchLoading }) => {
|
||||||
|
setSearchLoading(true);
|
||||||
|
|
||||||
|
dispatch(setPositionId(null));
|
||||||
|
const filterItemsId = itemsArr.map((item) => item.id).join();
|
||||||
|
const params = filterItemsId ? { skill: filterItemsId } : "";
|
||||||
|
|
||||||
|
apiRequest("/profile", {
|
||||||
|
params: { ...params, limit: 1000 },
|
||||||
|
}).then((res) => {
|
||||||
|
dispatch(profiles(res));
|
||||||
|
setSearchLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<section className="search">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-12">
|
||||||
|
<h2 className="search__title">Найти специалиста по навыкам</h2>
|
||||||
|
<div className="search__box">
|
||||||
|
<Select
|
||||||
|
value={itemsArr}
|
||||||
|
onChange={(value) => dispatch(selectedItems(value))}
|
||||||
|
isMulti
|
||||||
|
name="tags"
|
||||||
|
className="select"
|
||||||
|
classNamePrefix="select"
|
||||||
|
options={
|
||||||
|
tagsArr &&
|
||||||
|
tagsArr.flat().map((item) => {
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
value: item.value,
|
||||||
|
label: item.value,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => handleSubmit({ dispatch, setSearchLoading })}
|
||||||
|
type="submit"
|
||||||
|
className="search__submit"
|
||||||
|
>
|
||||||
|
{searchLoading ? <Loader width={30} height={30} /> : "Поиск"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TagSelect;
|
81
src/components/Select/tagSelect.scss
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
.search {
|
||||||
|
margin-top: 73px;
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-family: "GT Eesti Pro Display", sans-serif;
|
||||||
|
font-size: 2.4em;
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
letter-spacing: normal;
|
||||||
|
line-height: normal;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__box {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: white;
|
||||||
|
width: 131px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: none;
|
||||||
|
font-family: "Muller", sans-serif;
|
||||||
|
font-size: 1.6em;
|
||||||
|
letter-spacing: 0.8px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #73c141;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(0, 0, 0, 0);
|
||||||
|
color: #73c141;
|
||||||
|
box-shadow: inset 0 0 0 3px #73c141;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 575.98px) {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 575.98px) {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__submit {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select {
|
||||||
|
width: 85%;
|
||||||
|
|
||||||
|
@media (max-width: 990px) {
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[class$="-placeholder"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[class$="-control"] {
|
||||||
|
border-color: #e8e8e8 !important;
|
||||||
|
box-shadow: 0 0 0 0 #e8e8e8 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[class$="-value__label"] {
|
||||||
|
font-size: 1.4em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[class$="-option"] {
|
||||||
|
font-size: 1.6em !important;
|
||||||
|
color: gray !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search__submit:hover .loader path {
|
||||||
|
fill: #6aaf5c;
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { apiRequest } from "../../api/request";
|
import { apiRequest } from "../../api/request";
|
||||||
import {
|
import {
|
||||||
@ -11,8 +13,6 @@ import {
|
|||||||
getSendRequest,
|
getSendRequest,
|
||||||
setSendRequest,
|
setSendRequest,
|
||||||
} from "../../redux/reportSlice";
|
} from "../../redux/reportSlice";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import { Loader } from "../Loader/Loader";
|
import { Loader } from "../Loader/Loader";
|
||||||
|
|
||||||
import "./shortReport.scss";
|
import "./shortReport.scss";
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import Slider from "react-slick";
|
import Slider from "react-slick";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
import mockWorker from "../../images/mokPerson.png";
|
import mockWorker from "../../images/mokPerson.png";
|
||||||
|
|
||||||
import "./sliderWorkers.scss";
|
import "./sliderWorkers.scss";
|
||||||
import "slick-carousel/slick/slick.css";
|
import "slick-carousel/slick/slick.css";
|
||||||
import "slick-carousel/slick/slick-theme.css";
|
import "slick-carousel/slick/slick-theme.css";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
|
|
||||||
export const SliderWorkers = ({ title, titleInfo, subTitle }) => {
|
export const SliderWorkers = ({ title, titleInfo, subTitle }) => {
|
||||||
const [workers] = useState([
|
const [workers] = useState([
|
||||||
|
@ -1,44 +1,85 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from "react";
|
||||||
import './StarRating.scss'
|
import "./StarRating.scss";
|
||||||
|
|
||||||
const StarRating = ({ countStars=1, countActiveStars=1, color='#52B709', size=61 }) => {
|
const StarRating = ({
|
||||||
const [shadedStars, setShadedStars] = useState([])
|
countStars = 1,
|
||||||
const [noShadedStars, setNoShadedStars] = useState([])
|
countActiveStars = 1,
|
||||||
const percent = (Math.abs(countActiveStars)) >= countStars ? 100 : (countActiveStars * 100 / countStars)
|
color = "#52B709",
|
||||||
useEffect(() => {
|
size = 61,
|
||||||
for (let index = 0; index < countStars; index++) {
|
}) => {
|
||||||
setShadedStars(prev => [...prev, '★'])
|
const [shadedStars, setShadedStars] = useState([]);
|
||||||
setNoShadedStars(prev => [...prev, '☆'])
|
const [noShadedStars, setNoShadedStars] = useState([]);
|
||||||
}
|
const percent =
|
||||||
}, [])
|
Math.abs(countActiveStars) >= countStars
|
||||||
|
? 100
|
||||||
|
: (countActiveStars * 100) / countStars;
|
||||||
|
useEffect(() => {
|
||||||
|
for (let index = 0; index < countStars; index++) {
|
||||||
|
setShadedStars((prev) => [...prev, "★"]);
|
||||||
|
setNoShadedStars((prev) => [...prev, "☆"]);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
const ratingStyle = {
|
const ratingStyle = {
|
||||||
"--size": size+'px'
|
"--size": size + "px",
|
||||||
}
|
};
|
||||||
const activeStyle = {
|
const activeStyle = {
|
||||||
"--width": percent + '%',
|
"--width": percent + "%",
|
||||||
"--color": color,
|
"--color": color,
|
||||||
"--content": shadedStars.join('')
|
"--content": shadedStars.join(""),
|
||||||
}
|
};
|
||||||
const bodyStyle = {
|
const bodyStyle = {
|
||||||
"--content": noShadedStars.join(''),
|
"--content": noShadedStars.join(""),
|
||||||
"--color": color
|
"--color": color,
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='rating' style={ratingStyle}>
|
<div className="rating" style={ratingStyle}>
|
||||||
<div className="rating__body" style={bodyStyle} data-content={noShadedStars.join('')}>
|
<div
|
||||||
<div className="rating__active" style={activeStyle} data-content={shadedStars.join('')}></div>
|
className="rating__body"
|
||||||
<div className="rating__items">
|
style={bodyStyle}
|
||||||
<input type='radio' className="rating__item" value={1} name='star'></input>
|
data-content={noShadedStars.join("")}
|
||||||
<input type='radio' className="rating__item" value={2} name='star'></input>
|
>
|
||||||
<input type='radio' className="rating__item" value={3} name='star'></input>
|
<div
|
||||||
<input type='radio' className="rating__item" value={4} name='star'></input>
|
className="rating__active"
|
||||||
<input type='radio' className="rating__item" value={5} name='star'></input>
|
style={activeStyle}
|
||||||
</div>
|
data-content={shadedStars.join("")}
|
||||||
</div>
|
></div>
|
||||||
</div>
|
<div className="rating__items">
|
||||||
)
|
<input
|
||||||
}
|
type="radio"
|
||||||
|
className="rating__item"
|
||||||
|
value={1}
|
||||||
|
name="star"
|
||||||
|
></input>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
className="rating__item"
|
||||||
|
value={2}
|
||||||
|
name="star"
|
||||||
|
></input>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
className="rating__item"
|
||||||
|
value={3}
|
||||||
|
name="star"
|
||||||
|
></input>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
className="rating__item"
|
||||||
|
value={4}
|
||||||
|
name="star"
|
||||||
|
></input>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
className="rating__item"
|
||||||
|
value={5}
|
||||||
|
name="star"
|
||||||
|
></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default React.memo(StarRating);
|
export default React.memo(StarRating);
|
@ -1,45 +1,44 @@
|
|||||||
.rating{
|
.rating {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: var(--size);
|
font-size: var(--size);
|
||||||
line-height: 0.75;
|
line-height: 0.75;
|
||||||
&__body{
|
&__body {
|
||||||
position: relative;
|
position: relative;
|
||||||
&::before{
|
&::before {
|
||||||
content: attr(data-content);
|
content: attr(data-content);
|
||||||
display: block;
|
display: block;
|
||||||
color: var(--color);
|
color: var(--color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&__active{
|
&__active {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: var(--width);
|
width: var(--width);
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
&::before{
|
&::before {
|
||||||
content: attr(data-content);
|
content: attr(data-content);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
color: var(--color);
|
color: var(--color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&__items{
|
&__items {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
&__item{
|
&__item {
|
||||||
flex: 0 0 20%;
|
flex: 0 0 20%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import './stepForCandidate.scss'
|
|
||||||
|
|
||||||
export const StepsForCandidate = ({step}) => {
|
|
||||||
return(
|
|
||||||
<div className='step'>
|
|
||||||
<div className='step__start'>
|
|
||||||
<span>2</span>
|
|
||||||
<p>шага для твоего входа в команду </p>
|
|
||||||
</div>
|
|
||||||
<div className='step__info'>
|
|
||||||
<p>{step}</p>
|
|
||||||
<span>из 2</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
export default StepsForCandidate
|
|
20
src/components/StepsForCandidate/StepsForCandidate.jsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import "./stepForCandidate.scss";
|
||||||
|
|
||||||
|
export const StepsForCandidate = ({ step }) => {
|
||||||
|
return (
|
||||||
|
<div className="step">
|
||||||
|
<div className="step__start">
|
||||||
|
<span>2</span>
|
||||||
|
<p>шага для твоего входа в команду </p>
|
||||||
|
</div>
|
||||||
|
<div className="step__info">
|
||||||
|
<p>{step}</p>
|
||||||
|
<span>из 2</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StepsForCandidate;
|
@ -4,7 +4,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
top: -100px;
|
top: -100px;
|
||||||
padding:0 55px 0 85px;
|
padding: 0 55px 0 85px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
@media (max-width: 965px) {
|
@media (max-width: 965px) {
|
||||||
@ -20,7 +20,7 @@
|
|||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
font-size: 258px;
|
font-size: 258px;
|
||||||
line-height: 32px;
|
line-height: 32px;
|
||||||
color: #52B709;
|
color: #52b709;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@ -41,7 +41,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
p {
|
p {
|
||||||
background: #DDEEC6;
|
background: #ddeec6;
|
||||||
border-radius: 44px;
|
border-radius: 44px;
|
||||||
padding: 8px 26px;
|
padding: 8px 26px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
|
|
||||||
import './taskItem.scss'
|
|
||||||
|
|
||||||
export const TaskItem = ({ index, text, hours }) => {
|
|
||||||
return (
|
|
||||||
<div className='task-item'>
|
|
||||||
<div className='task-item__index'>{index}.</div>
|
|
||||||
<div className='task-item__text'>{text}</div>
|
|
||||||
<div className='task-item__hours'>
|
|
||||||
<div className='task-item__hours-value'>{hours}</div>
|
|
||||||
<div className='task-item__hours-text'>Количество часов</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
16
src/components/TaskItem/TaskItem.jsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import "./taskItem.scss";
|
||||||
|
|
||||||
|
export const TaskItem = ({ index, text, hours }) => {
|
||||||
|
return (
|
||||||
|
<div className="task-item">
|
||||||
|
<div className="task-item__index">{index}.</div>
|
||||||
|
<div className="task-item__text">{text}</div>
|
||||||
|
<div className="task-item__hours">
|
||||||
|
<div className="task-item__hours-value">{hours}</div>
|
||||||
|
<div className="task-item__hours-text">Количество часов</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -6,7 +6,7 @@
|
|||||||
&__index {
|
&__index {
|
||||||
margin-top: 0.2rem;
|
margin-top: 0.2rem;
|
||||||
color: #282828;
|
color: #282828;
|
||||||
font-family: 'GT Eesti Pro Display';
|
font-family: "GT Eesti Pro Display";
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 48.74px;
|
line-height: 48.74px;
|
||||||
@ -19,7 +19,7 @@
|
|||||||
max-width: 525px;
|
max-width: 525px;
|
||||||
margin-left: 1.6rem;
|
margin-left: 1.6rem;
|
||||||
color: #000000;
|
color: #000000;
|
||||||
font-family: 'GT Eesti Pro Display';
|
font-family: "GT Eesti Pro Display";
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
letter-spacing: normal;
|
letter-spacing: normal;
|
||||||
@ -49,7 +49,7 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-family: 'Muller Extra Bold';
|
font-family: "Muller Extra Bold";
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@ -61,7 +61,7 @@
|
|||||||
width: 69px;
|
width: 69px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
color: #000000;
|
color: #000000;
|
||||||
font-family: 'GT Eesti Pro Display - Thin';
|
font-family: "GT Eesti Pro Display - Thin";
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
letter-spacing: normal;
|
letter-spacing: normal;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import cardCalendar from "../../../images/cardCalendar.svg";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
import cardCalendar from "../../../images/cardCalendar.svg";
|
||||||
|
|
||||||
import "./cardArticle.scss";
|
import "./cardArticle.scss";
|
||||||
|
|
||||||
export const CardArticle = ({ images, title, data, id }) => {
|
export const CardArticle = ({ images, title, data, id }) => {
|
||||||
|
@ -4,8 +4,8 @@ import { Link } from "react-router-dom";
|
|||||||
import ModalLayout from "../ModalLayout/ModalLayout";
|
import ModalLayout from "../ModalLayout/ModalLayout";
|
||||||
|
|
||||||
import avatar from "../../../images/mokPerson.png";
|
import avatar from "../../../images/mokPerson.png";
|
||||||
import logoTg from "../../../images/TgLogo.svg";
|
import logoTg from "../../../images/tgLogo.svg";
|
||||||
import arrow from "../../../images/right-arrow.png";
|
import arrow from "../../../images/left-arrow.png";
|
||||||
import interview from "../../../images/interviewLogo.svg";
|
import interview from "../../../images/interviewLogo.svg";
|
||||||
|
|
||||||
import "./modalAspt.scss";
|
import "./modalAspt.scss";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import telegramLogo from "../../../images/TgLogo.svg";
|
import telegramLogo from "../../../images/tgLogo.svg";
|
||||||
import doc from "../../../images/doc.svg";
|
import doc from "../../../images/doc.svg";
|
||||||
import anyMoment from "../../../images/anyMoment.svg";
|
import anyMoment from "../../../images/anyMoment.svg";
|
||||||
|
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
import React, {useEffect, useState} from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import TrackerModal from "../TrackerModal/TrackerModal";
|
import TrackerModal from "../TrackerModal/TrackerModal";
|
||||||
import { apiRequest } from "../../../api/request";
|
import { apiRequest } from "../../../api/request";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
|
import { urlForLocal } from "../../../helper";
|
||||||
import {
|
import {
|
||||||
modalToggle,
|
modalToggle,
|
||||||
setProjectBoardFetch,
|
setProjectBoardFetch,
|
||||||
} from "../../../redux/projectsTrackerSlice";
|
} from "../../../redux/projectsTrackerSlice";
|
||||||
|
import { getCorrectDate } from "../../../components/Calendar/calendarHelper";
|
||||||
|
|
||||||
import {getCorrectDate} from '../../../components/Calendar/calendarHelper'
|
import category from "../../../images/category.svg";
|
||||||
|
import watch from "../../../images/watch.svg";
|
||||||
import category from "../../../images/category.png";
|
|
||||||
import watch from "../../../images/watch.png";
|
|
||||||
import file from "../../../images/fileModal.svg";
|
import file from "../../../images/fileModal.svg";
|
||||||
import arrow from "../../../images/arrowStart.png";
|
import arrow from "../../../images/arrowStart.png";
|
||||||
import link from "../../../images/link.svg";
|
import link from "../../../images/link.svg";
|
||||||
@ -24,7 +25,6 @@ import fullScreen from "../../../images/inFullScreen.svg";
|
|||||||
import close from "../../../images/closeProjectPersons.svg";
|
import close from "../../../images/closeProjectPersons.svg";
|
||||||
|
|
||||||
import "./ModalTicket.scss";
|
import "./ModalTicket.scss";
|
||||||
import {urlForLocal} from "../../../helper";
|
|
||||||
|
|
||||||
export const ModalTiсket = ({
|
export const ModalTiсket = ({
|
||||||
active,
|
active,
|
||||||
@ -32,20 +32,24 @@ export const ModalTiсket = ({
|
|||||||
task,
|
task,
|
||||||
projectId,
|
projectId,
|
||||||
projectName,
|
projectName,
|
||||||
projectUsers
|
projectUsers,
|
||||||
}) => {
|
}) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [addSubtask, setAddSubtask] = useState(false);
|
const [addSubtask, setAddSubtask] = useState(false);
|
||||||
const [editOpen, setEditOpen] = useState(false);
|
const [editOpen, setEditOpen] = useState(false);
|
||||||
const [inputsValue, setInputsValue] = useState({title: task.title, description: task.description, comment: ''});
|
const [inputsValue, setInputsValue] = useState({
|
||||||
|
title: task.title,
|
||||||
|
description: task.description,
|
||||||
|
comment: "",
|
||||||
|
});
|
||||||
const [comments, setComments] = useState([]);
|
const [comments, setComments] = useState([]);
|
||||||
const [commentsEditOpen, setCommentsEditOpen] = useState({})
|
const [commentsEditOpen, setCommentsEditOpen] = useState({});
|
||||||
const [commentsEditText, setCommentsEditText] = useState({})
|
const [commentsEditText, setCommentsEditText] = useState({});
|
||||||
const [dropListOpen, setDropListOpen] = useState(false)
|
const [dropListOpen, setDropListOpen] = useState(false);
|
||||||
const [dropListMembersOpen, setDropListMembersOpen] = useState(false)
|
const [dropListMembersOpen, setDropListMembersOpen] = useState(false);
|
||||||
const [executor, setExecutor] = useState(task.executor)
|
const [executor, setExecutor] = useState(task.executor);
|
||||||
const [members, setMembers] = useState(task.taskUsers)
|
const [members, setMembers] = useState(task.taskUsers);
|
||||||
const [users, setUsers] = useState([])
|
const [users, setUsers] = useState([]);
|
||||||
|
|
||||||
function deleteTask() {
|
function deleteTask() {
|
||||||
apiRequest("/task/update-task", {
|
apiRequest("/task/update-task", {
|
||||||
@ -66,7 +70,7 @@ export const ModalTiсket = ({
|
|||||||
data: {
|
data: {
|
||||||
task_id: task.id,
|
task_id: task.id,
|
||||||
title: inputsValue.title,
|
title: inputsValue.title,
|
||||||
description: inputsValue.description
|
description: inputsValue.description,
|
||||||
},
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
dispatch(setProjectBoardFetch(projectId));
|
dispatch(setProjectBoardFetch(projectId));
|
||||||
@ -79,39 +83,42 @@ export const ModalTiсket = ({
|
|||||||
data: {
|
data: {
|
||||||
text: inputsValue.comment,
|
text: inputsValue.comment,
|
||||||
entity_type: 2,
|
entity_type: 2,
|
||||||
entity_id: task.id
|
entity_id: task.id,
|
||||||
}
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
let newComment = res
|
let newComment = res;
|
||||||
newComment.created_at = new Date()
|
newComment.created_at = new Date();
|
||||||
setInputsValue((prevValue) => ({...prevValue, comment: ''}))
|
setInputsValue((prevValue) => ({ ...prevValue, comment: "" }));
|
||||||
setComments((prevValue) => ([...prevValue, newComment]))
|
setComments((prevValue) => [...prevValue, newComment]);
|
||||||
setCommentsEditOpen((prevValue) => ({...prevValue, [res.id]: false}))
|
setCommentsEditOpen((prevValue) => ({ ...prevValue, [res.id]: false }));
|
||||||
setCommentsEditText((prevValue) => ({...prevValue, [res.id]: res.text}))
|
setCommentsEditText((prevValue) => ({
|
||||||
})
|
...prevValue,
|
||||||
|
[res.id]: res.text,
|
||||||
|
}));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function deleteComment(commentId) {
|
function deleteComment(commentId) {
|
||||||
apiRequest("/comment/update", {
|
apiRequest("/comment/update", {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
data: {
|
data: {
|
||||||
comment_id: commentId,
|
comment_id: commentId,
|
||||||
status: 0
|
status: 0,
|
||||||
}
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
setComments((prevValue) => prevValue.filter((item) => item.id !== commentId))
|
setComments((prevValue) =>
|
||||||
})
|
prevValue.filter((item) => item.id !== commentId)
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function editComment(commentId) {
|
function editComment(commentId) {
|
||||||
|
|
||||||
apiRequest("/comment/update", {
|
apiRequest("/comment/update", {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
data: {
|
data: {
|
||||||
comment_id: commentId,
|
comment_id: commentId,
|
||||||
text: commentsEditText[commentId]
|
text: commentsEditText[commentId],
|
||||||
}
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {});
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function taskExecutor(person) {
|
function taskExecutor(person) {
|
||||||
@ -119,11 +126,11 @@ export const ModalTiсket = ({
|
|||||||
method: "PUT",
|
method: "PUT",
|
||||||
data: {
|
data: {
|
||||||
task_id: task.id,
|
task_id: task.id,
|
||||||
executor_id: person.user_id
|
executor_id: person.user_id,
|
||||||
},
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
setDropListOpen(false)
|
setDropListOpen(false);
|
||||||
setExecutor(res.executor)
|
setExecutor(res.executor);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,10 +139,10 @@ export const ModalTiсket = ({
|
|||||||
method: "PUT",
|
method: "PUT",
|
||||||
data: {
|
data: {
|
||||||
task_id: task.id,
|
task_id: task.id,
|
||||||
executor_id: 0
|
executor_id: 0,
|
||||||
},
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
setExecutor(null)
|
setExecutor(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,11 +151,11 @@ export const ModalTiсket = ({
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
data: {
|
data: {
|
||||||
task_id: task.id,
|
task_id: task.id,
|
||||||
user_id: person.user_id
|
user_id: person.user_id,
|
||||||
},
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
setDropListMembersOpen(false)
|
setDropListMembersOpen(false);
|
||||||
setMembers((prevValue) => ([...prevValue, res]))
|
setMembers((prevValue) => [...prevValue, res]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,31 +164,40 @@ export const ModalTiсket = ({
|
|||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
data: {
|
data: {
|
||||||
task_id: task.id,
|
task_id: task.id,
|
||||||
user_id: person.user_id
|
user_id: person.user_id,
|
||||||
},
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
setMembers(members.filter((item) => item.user_id !== person.user_id))
|
setMembers(members.filter((item) => item.user_id !== person.user_id));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
apiRequest(`/comment/get-by-entity?entity_type=2&entity_id=${task.id}`).then((res) => {
|
apiRequest(
|
||||||
setComments(res)
|
`/comment/get-by-entity?entity_type=2&entity_id=${task.id}`
|
||||||
|
).then((res) => {
|
||||||
|
setComments(res);
|
||||||
res.forEach((item) => {
|
res.forEach((item) => {
|
||||||
setCommentsEditOpen((prevValue) => ({...prevValue, [item.id]: false}))
|
setCommentsEditOpen((prevValue) => ({
|
||||||
setCommentsEditText((prevValue) => ({...prevValue, [item.id]: item.text}))
|
...prevValue,
|
||||||
})
|
[item.id]: false,
|
||||||
})
|
}));
|
||||||
}, [])
|
setCommentsEditText((prevValue) => ({
|
||||||
|
...prevValue,
|
||||||
|
[item.id]: item.text,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let ids = members.map((user) => user.user_id)
|
let ids = members.map((user) => user.user_id);
|
||||||
setUsers(projectUsers.reduce((acc, cur) => {
|
setUsers(
|
||||||
if (!ids.includes(cur.user_id)) acc.push(cur)
|
projectUsers.reduce((acc, cur) => {
|
||||||
return acc
|
if (!ids.includes(cur.user_id)) acc.push(cur);
|
||||||
}, []))
|
return acc;
|
||||||
}, [members])
|
}, [])
|
||||||
|
);
|
||||||
|
}, [members]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -206,13 +222,33 @@ export const ModalTiсket = ({
|
|||||||
|
|
||||||
<div className="content__task">
|
<div className="content__task">
|
||||||
<span>Задача</span>
|
<span>Задача</span>
|
||||||
{editOpen ? <input value={inputsValue.title} onChange={(e) => {
|
{editOpen ? (
|
||||||
setInputsValue((prevValue) => ({...prevValue, title: e.target.value}))
|
<input
|
||||||
}} /> :<h5>{inputsValue.title}</h5>}
|
value={inputsValue.title}
|
||||||
|
onChange={(e) => {
|
||||||
|
setInputsValue((prevValue) => ({
|
||||||
|
...prevValue,
|
||||||
|
title: e.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<h5>{inputsValue.title}</h5>
|
||||||
|
)}
|
||||||
<div className="content__description">
|
<div className="content__description">
|
||||||
{editOpen ? <input value={inputsValue.description} onChange={(e) => {
|
{editOpen ? (
|
||||||
setInputsValue((prevValue) => ({...prevValue, description: e.target.value}))
|
<input
|
||||||
}}/> :<p>{inputsValue.description}</p>}
|
value={inputsValue.description}
|
||||||
|
onChange={(e) => {
|
||||||
|
setInputsValue((prevValue) => ({
|
||||||
|
...prevValue,
|
||||||
|
description: e.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<p>{inputsValue.description}</p>
|
||||||
|
)}
|
||||||
{/*<img src={taskImg} className="image-task"></img>*/}
|
{/*<img src={taskImg} className="image-task"></img>*/}
|
||||||
</div>
|
</div>
|
||||||
<div className="content__communication">
|
<div className="content__communication">
|
||||||
@ -237,97 +273,163 @@ export const ModalTiсket = ({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="content__input">
|
<div className="content__input">
|
||||||
<input placeholder="Оставить комментарий" value={inputsValue.comment} onChange={(e) => {
|
<input
|
||||||
setInputsValue((prevValue) => ({...prevValue, comment: e.target.value}))
|
placeholder="Оставить комментарий"
|
||||||
}} />
|
value={inputsValue.comment}
|
||||||
|
onChange={(e) => {
|
||||||
|
setInputsValue((prevValue) => ({
|
||||||
|
...prevValue,
|
||||||
|
comment: e.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<img src={send} onClick={createComment}></img>
|
<img src={send} onClick={createComment}></img>
|
||||||
</div>
|
</div>
|
||||||
<div className='comments__list'>
|
<div className="comments__list">
|
||||||
{comments.map((comment) => {
|
{comments.map((comment) => {
|
||||||
return <div className='comments__list__item' key={comment.id}>
|
return (
|
||||||
<div className='comments__list__item__info'>
|
<div className="comments__list__item" key={comment.id}>
|
||||||
<span>{getCorrectDate(comment.created_at)}</span>
|
<div className="comments__list__item__info">
|
||||||
<div className={commentsEditOpen[comment.id] ? 'edit edit__open' : 'edit'} >
|
<span>{getCorrectDate(comment.created_at)}</span>
|
||||||
<img src={edit} alt='edit' onClick={() => {
|
<div
|
||||||
if (commentsEditOpen[comment.id]) {
|
className={
|
||||||
editComment(comment.id)
|
commentsEditOpen[comment.id]
|
||||||
|
? "edit edit__open"
|
||||||
|
: "edit"
|
||||||
}
|
}
|
||||||
setCommentsEditOpen((prevValue) => ({...prevValue, [comment.id]: !prevValue[comment.id]}))
|
>
|
||||||
}} />
|
<img
|
||||||
|
src={edit}
|
||||||
|
alt="edit"
|
||||||
|
onClick={() => {
|
||||||
|
if (commentsEditOpen[comment.id]) {
|
||||||
|
editComment(comment.id);
|
||||||
|
}
|
||||||
|
setCommentsEditOpen((prevValue) => ({
|
||||||
|
...prevValue,
|
||||||
|
[comment.id]: !prevValue[comment.id],
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
src={del}
|
||||||
|
alt="delete"
|
||||||
|
onClick={() => deleteComment(comment.id)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<img src={del} alt='delete' onClick={() => deleteComment(comment.id)} />
|
{commentsEditOpen[comment.id] ? (
|
||||||
|
<input
|
||||||
|
value={commentsEditText[comment.id]}
|
||||||
|
onChange={(e) => {
|
||||||
|
setCommentsEditText((prevValue) => ({
|
||||||
|
...prevValue,
|
||||||
|
[comment.id]: e.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<p>{commentsEditText[comment.id]}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{commentsEditOpen[comment.id] ? <input value={commentsEditText[comment.id]} onChange={(e) => {
|
);
|
||||||
setCommentsEditText((prevValue) => ({...prevValue, [comment.id]: e.target.value}))
|
})}
|
||||||
}} /> : <p>{commentsEditText[comment.id]}</p>}
|
|
||||||
</div>
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="workers">
|
<div className="workers">
|
||||||
<div className="workers_box task__info">
|
<div className="workers_box task__info">
|
||||||
<span className="exit" onClick={() => setActive(false)}></span>
|
<span className="exit" onClick={() => setActive(false)}></span>
|
||||||
<span className='nameProject'>{task.title}</span>
|
<span className="nameProject">{task.title}</span>
|
||||||
<p className="workers__creator">Создатель : {task.user?.fio}</p>
|
<p className="workers__creator">Создатель : {task.user?.fio}</p>
|
||||||
|
|
||||||
{executor ?
|
{executor ? (
|
||||||
<div className='executor'>
|
<div className="executor">
|
||||||
<p>Исполнитель: {executor.fio}</p>
|
<p>Исполнитель: {executor.fio}</p>
|
||||||
<img src={urlForLocal(executor.avatar)} alt='avatar' />
|
<img src={urlForLocal(executor.avatar)} alt="avatar" />
|
||||||
<img src={close} className='delete' onClick={() => deleteTaskExecutor()} />
|
<img
|
||||||
</div> :
|
src={close}
|
||||||
|
className="delete"
|
||||||
|
onClick={() => deleteTaskExecutor()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<div className="add-worker moreItems ">
|
<div className="add-worker moreItems ">
|
||||||
<button onClick={() => setDropListOpen(true)}>+</button>
|
<button onClick={() => setDropListOpen(true)}>+</button>
|
||||||
<span>Добавить исполнителя</span>
|
<span>Добавить исполнителя</span>
|
||||||
{dropListOpen &&
|
{dropListOpen && (
|
||||||
<div className='dropdownList'>
|
<div className="dropdownList">
|
||||||
<img src={close} className='dropdownList__close' onClick={() => setDropListOpen(false)} />
|
<img
|
||||||
|
src={close}
|
||||||
|
className="dropdownList__close"
|
||||||
|
onClick={() => setDropListOpen(false)}
|
||||||
|
/>
|
||||||
{projectUsers.map((person) => {
|
{projectUsers.map((person) => {
|
||||||
return <div className='dropdownList__person' key={person.user_id} onClick={() => taskExecutor(person)}>
|
return (
|
||||||
<span>{person.user.fio}</span>
|
<div
|
||||||
<img src={urlForLocal(person.user.avatar)} />
|
className="dropdownList__person"
|
||||||
</div>
|
key={person.user_id}
|
||||||
})
|
onClick={() => taskExecutor(person)}
|
||||||
}
|
>
|
||||||
|
<span>{person.user.fio}</span>
|
||||||
|
<img src={urlForLocal(person.user.avatar)} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
|
|
||||||
{Boolean(members.length) &&
|
{Boolean(members.length) && (
|
||||||
<div className='members'>
|
<div className="members">
|
||||||
<p>Участники:</p>
|
<p>Участники:</p>
|
||||||
<div className='members__list'>
|
<div className="members__list">
|
||||||
{members.map((member) => {
|
{members.map((member) => {
|
||||||
return <div className='worker' key={member.user_id}>
|
return (
|
||||||
<p>{member.fio}</p>
|
<div className="worker" key={member.user_id}>
|
||||||
<img src={urlForLocal(member.avatar)} />
|
<p>{member.fio}</p>
|
||||||
<img src={close} className='delete' onClick={() => deleteMember(member)} />
|
<img src={urlForLocal(member.avatar)} />
|
||||||
</div>
|
<img
|
||||||
})
|
src={close}
|
||||||
}
|
className="delete"
|
||||||
|
onClick={() => deleteMember(member)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
|
|
||||||
<div className="add-worker moreItems">
|
<div className="add-worker moreItems">
|
||||||
<button onClick={() => setDropListMembersOpen(true)}>+</button>
|
<button onClick={() => setDropListMembersOpen(true)}>+</button>
|
||||||
<span>Добавить участников</span>
|
<span>Добавить участников</span>
|
||||||
{dropListMembersOpen &&
|
{dropListMembersOpen && (
|
||||||
<div className='dropdownList'>
|
<div className="dropdownList">
|
||||||
<img src={close} className='dropdownList__close' onClick={() => setDropListMembersOpen(false)} />
|
<img
|
||||||
{users.length ? users.map((person) => {
|
src={close}
|
||||||
return <div className='dropdownList__person' key={person.user_id} onClick={() => addMember(person)}>
|
className="dropdownList__close"
|
||||||
<span>{person.user.fio}</span>
|
onClick={() => setDropListMembersOpen(false)}
|
||||||
<img src={urlForLocal(person.user.avatar)} />
|
/>
|
||||||
</div>
|
{users.length ? (
|
||||||
}) : <p className='noUsers'>Нет пользователей</p>
|
users.map((person) => {
|
||||||
}
|
return (
|
||||||
</div>
|
<div
|
||||||
}
|
className="dropdownList__person"
|
||||||
|
key={person.user_id}
|
||||||
|
onClick={() => addMember(person)}
|
||||||
|
>
|
||||||
|
<span>{person.user.fio}</span>
|
||||||
|
<img src={urlForLocal(person.user.avatar)} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<p className="noUsers">Нет пользователей</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -344,16 +446,19 @@ export const ModalTiсket = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="workers_box-bottom">
|
<div className="workers_box-bottom">
|
||||||
<div className={editOpen ? 'edit' : ''} onClick={() => {
|
<div
|
||||||
if(editOpen) {
|
className={editOpen ? "edit" : ""}
|
||||||
setEditOpen(!editOpen)
|
onClick={() => {
|
||||||
editTask()
|
if (editOpen) {
|
||||||
} else {
|
setEditOpen(!editOpen);
|
||||||
setEditOpen(!editOpen)
|
editTask();
|
||||||
}
|
} else {
|
||||||
}}>
|
setEditOpen(!editOpen);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<img src={edit}></img>
|
<img src={edit}></img>
|
||||||
<p>{editOpen ? 'сохранить' : 'редактировать'}</p>
|
<p>{editOpen ? "сохранить" : "редактировать"}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<img src={link}></img>
|
<img src={link}></img>
|
||||||
|
@ -6,23 +6,24 @@ import { Footer } from "../../Footer/Footer";
|
|||||||
import { Link, useParams, useNavigate } from "react-router-dom";
|
import { Link, useParams, useNavigate } from "react-router-dom";
|
||||||
import TrackerModal from "../TrackerModal/TrackerModal";
|
import TrackerModal from "../TrackerModal/TrackerModal";
|
||||||
import { Navigation } from "../../Navigation/Navigation";
|
import { Navigation } from "../../Navigation/Navigation";
|
||||||
import {Loader} from "../../Loader/Loader";
|
import { Loader } from "../../Loader/Loader";
|
||||||
|
|
||||||
import {useDispatch, useSelector} from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import {
|
import {
|
||||||
deletePersonOnProject,
|
deletePersonOnProject,
|
||||||
modalToggle,
|
modalToggle,
|
||||||
setProjectBoardFetch,
|
setProjectBoardFetch,
|
||||||
setToggleTab,
|
setToggleTab,
|
||||||
getProjectBoard,
|
getProjectBoard,
|
||||||
getBoarderLoader
|
getBoarderLoader,
|
||||||
} from "../../../redux/projectsTrackerSlice";
|
} from "../../../redux/projectsTrackerSlice";
|
||||||
import { apiRequest } from "../../../api/request";
|
import { apiRequest } from "../../../api/request";
|
||||||
|
import { urlForLocal } from "../../../helper";
|
||||||
|
import { getCorrectDate } from "../../Calendar/calendarHelper";
|
||||||
|
|
||||||
import project from "../../../images/trackerProject.svg";
|
import project from "../../../images/trackerProject.svg";
|
||||||
import watch from "../../../images/watch.png";
|
import watch from "../../../images/watch.svg";
|
||||||
import file from "../../../images/fileModal.svg";
|
import file from "../../../images/fileModal.svg";
|
||||||
import task from "../../../images/tasksMock.png";
|
|
||||||
import send from "../../../images/send.svg";
|
import send from "../../../images/send.svg";
|
||||||
import arrow2 from "../../../images/arrowStart.png";
|
import arrow2 from "../../../images/arrowStart.png";
|
||||||
import plus from "../../../images/plus.svg";
|
import plus from "../../../images/plus.svg";
|
||||||
@ -34,11 +35,9 @@ import link from "../../../images/link.svg";
|
|||||||
import archive2 from "../../../images/archive.svg";
|
import archive2 from "../../../images/archive.svg";
|
||||||
import del from "../../../images/delete.svg";
|
import del from "../../../images/delete.svg";
|
||||||
import edit from "../../../images/edit.svg";
|
import edit from "../../../images/edit.svg";
|
||||||
|
import close from "../../../images/closeProjectPersons.svg";
|
||||||
|
|
||||||
import "./ticketFullScreen.scss";
|
import "./ticketFullScreen.scss";
|
||||||
import close from "../../../images/closeProjectPersons.svg";
|
|
||||||
import {urlForLocal} from "../../../helper";
|
|
||||||
import {getCorrectDate} from "../../Calendar/calendarHelper";
|
|
||||||
|
|
||||||
export const TicketFullScreen = ({}) => {
|
export const TicketFullScreen = ({}) => {
|
||||||
const [modalAddWorker, setModalAddWorker] = useState(false);
|
const [modalAddWorker, setModalAddWorker] = useState(false);
|
||||||
@ -52,23 +51,35 @@ export const TicketFullScreen = ({}) => {
|
|||||||
const [inputsValue, setInputsValue] = useState({});
|
const [inputsValue, setInputsValue] = useState({});
|
||||||
const [loader, setLoader] = useState(true);
|
const [loader, setLoader] = useState(true);
|
||||||
const [comments, setComments] = useState([]);
|
const [comments, setComments] = useState([]);
|
||||||
const [commentsEditOpen, setCommentsEditOpen] = useState({})
|
const [commentsEditOpen, setCommentsEditOpen] = useState({});
|
||||||
const [commentsEditText, setCommentsEditText] = useState({})
|
const [commentsEditText, setCommentsEditText] = useState({});
|
||||||
const [personListOpen, setPersonListOpen] = useState(false)
|
const [personListOpen, setPersonListOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
apiRequest(`/task/get-task?task_id=${ticketId.id}`).then((taskInfo) => {
|
apiRequest(`/task/get-task?task_id=${ticketId.id}`).then((taskInfo) => {
|
||||||
setTaskInfo(taskInfo);
|
setTaskInfo(taskInfo);
|
||||||
setInputsValue({title: taskInfo.title, description: taskInfo.description, comment: ''})
|
setInputsValue({
|
||||||
apiRequest(`/comment/get-by-entity?entity_type=2&entity_id=${taskInfo.id}`).then((res) => {
|
title: taskInfo.title,
|
||||||
setComments(res)
|
description: taskInfo.description,
|
||||||
|
comment: "",
|
||||||
|
});
|
||||||
|
apiRequest(
|
||||||
|
`/comment/get-by-entity?entity_type=2&entity_id=${taskInfo.id}`
|
||||||
|
).then((res) => {
|
||||||
|
setComments(res);
|
||||||
res.forEach((item) => {
|
res.forEach((item) => {
|
||||||
setCommentsEditOpen((prevValue) => ({...prevValue, [item.id]: false}))
|
setCommentsEditOpen((prevValue) => ({
|
||||||
setCommentsEditText((prevValue) => ({...prevValue, [item.id]: item.text}))
|
...prevValue,
|
||||||
})
|
[item.id]: false,
|
||||||
})
|
}));
|
||||||
|
setCommentsEditText((prevValue) => ({
|
||||||
|
...prevValue,
|
||||||
|
[item.id]: item.text,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
dispatch(setProjectBoardFetch(taskInfo.project_id));
|
dispatch(setProjectBoardFetch(taskInfo.project_id));
|
||||||
setLoader(boardLoader)
|
setLoader(boardLoader);
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -90,10 +101,9 @@ export const TicketFullScreen = ({}) => {
|
|||||||
data: {
|
data: {
|
||||||
task_id: taskInfo.id,
|
task_id: taskInfo.id,
|
||||||
title: inputsValue.title,
|
title: inputsValue.title,
|
||||||
description: inputsValue.description
|
description: inputsValue.description,
|
||||||
},
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createComment() {
|
function createComment() {
|
||||||
@ -102,16 +112,19 @@ export const TicketFullScreen = ({}) => {
|
|||||||
data: {
|
data: {
|
||||||
text: inputsValue.comment,
|
text: inputsValue.comment,
|
||||||
entity_type: 2,
|
entity_type: 2,
|
||||||
entity_id: taskInfo.id
|
entity_id: taskInfo.id,
|
||||||
}
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
let newComment = res
|
let newComment = res;
|
||||||
newComment.created_at = new Date()
|
newComment.created_at = new Date();
|
||||||
setInputsValue((prevValue) => ({...prevValue, comment: ''}))
|
setInputsValue((prevValue) => ({ ...prevValue, comment: "" }));
|
||||||
setComments((prevValue) => ([...prevValue, newComment]))
|
setComments((prevValue) => [...prevValue, newComment]);
|
||||||
setCommentsEditOpen((prevValue) => ({...prevValue, [res.id]: false}))
|
setCommentsEditOpen((prevValue) => ({ ...prevValue, [res.id]: false }));
|
||||||
setCommentsEditText((prevValue) => ({...prevValue, [res.id]: res.text}))
|
setCommentsEditText((prevValue) => ({
|
||||||
})
|
...prevValue,
|
||||||
|
[res.id]: res.text,
|
||||||
|
}));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteComment(commentId) {
|
function deleteComment(commentId) {
|
||||||
@ -119,11 +132,13 @@ export const TicketFullScreen = ({}) => {
|
|||||||
method: "PUT",
|
method: "PUT",
|
||||||
data: {
|
data: {
|
||||||
comment_id: commentId,
|
comment_id: commentId,
|
||||||
status: 0
|
status: 0,
|
||||||
}
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
setComments((prevValue) => prevValue.filter((item) => item.id !== commentId))
|
setComments((prevValue) =>
|
||||||
})
|
prevValue.filter((item) => item.id !== commentId)
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function editComment(commentId) {
|
function editComment(commentId) {
|
||||||
@ -131,10 +146,9 @@ export const TicketFullScreen = ({}) => {
|
|||||||
method: "PUT",
|
method: "PUT",
|
||||||
data: {
|
data: {
|
||||||
comment_id: commentId,
|
comment_id: commentId,
|
||||||
text: commentsEditText[commentId]
|
text: commentsEditText[commentId],
|
||||||
}
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {});
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deletePerson(userId) {
|
function deletePerson(userId) {
|
||||||
@ -142,10 +156,10 @@ export const TicketFullScreen = ({}) => {
|
|||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
data: {
|
data: {
|
||||||
project_id: projectBoard.id,
|
project_id: projectBoard.id,
|
||||||
user_id: userId
|
user_id: userId,
|
||||||
},
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
dispatch(deletePersonOnProject(userId))
|
dispatch(deletePersonOnProject(userId));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,223 +209,308 @@ export const TicketFullScreen = ({}) => {
|
|||||||
<p>Архив</p>
|
<p>Архив</p>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{loader ? <Loader /> :
|
{loader ? (
|
||||||
<>
|
<Loader />
|
||||||
<div className="tracker__tabs__content content-tabs">
|
) : (
|
||||||
<div className="tasks__head">
|
<>
|
||||||
<div className="tasks__head__wrapper">
|
<div className="tracker__tabs__content content-tabs">
|
||||||
<h4>Проект : {projectBoard.name}</h4>
|
<div className="tasks__head">
|
||||||
|
<div className="tasks__head__wrapper">
|
||||||
|
<h4>Проект : {projectBoard.name}</h4>
|
||||||
|
|
||||||
<TrackerModal
|
<TrackerModal
|
||||||
active={modalAddWorker}
|
active={modalAddWorker}
|
||||||
setActive={setModalAddWorker}
|
setActive={setModalAddWorker}
|
||||||
></TrackerModal>
|
></TrackerModal>
|
||||||
|
|
||||||
<div className="tasks__head__persons">
|
<div className="tasks__head__persons">
|
||||||
{/*<img src={avatarTest} alt="avatar" />*/}
|
{/*<img src={avatarTest} alt="avatar" />*/}
|
||||||
{/*<img src={avatarTest} alt="avatar" />*/}
|
{/*<img src={avatarTest} alt="avatar" />*/}
|
||||||
<span className="countPersons">{projectBoard.projectUsers?.length}</span>
|
<span className="countPersons">
|
||||||
<span
|
{projectBoard.projectUsers?.length}
|
||||||
className="addPerson"
|
</span>
|
||||||
onClick={() => {
|
<span
|
||||||
setPersonListOpen(true)
|
className="addPerson"
|
||||||
}}
|
onClick={() => {
|
||||||
>
|
setPersonListOpen(true);
|
||||||
+
|
}}
|
||||||
</span>
|
>
|
||||||
<p>добавить участника</p>
|
+
|
||||||
{personListOpen &&
|
</span>
|
||||||
<div className='persons__list'>
|
<p>добавить участника</p>
|
||||||
<img className='persons__list__close' src={close} alt='close' onClick={() => setPersonListOpen(false)} />
|
{personListOpen && (
|
||||||
<div className='persons__list__count'><span>{projectBoard.projectUsers?.length}</span>участник</div>
|
<div className="persons__list">
|
||||||
<div className='persons__list__info'>В проекте - <span>“{projectBoard.name}”</span></div>
|
<img
|
||||||
<div className='persons__list__items'>
|
className="persons__list__close"
|
||||||
{projectBoard.projectUsers?.map((person) => {
|
src={close}
|
||||||
return <div className='persons__list__item' key={person.user_id}>
|
alt="close"
|
||||||
<img className='avatar' src={urlForLocal(person.user.avatar)} alt='avatar' />
|
onClick={() => setPersonListOpen(false)}
|
||||||
<span>{person.user.fio}</span>
|
/>
|
||||||
<img className='delete' src={close} alt='delete' onClick={() => deletePerson(person.user_id)}/>
|
<div className="persons__list__count">
|
||||||
|
<span>{projectBoard.projectUsers?.length}</span>
|
||||||
|
участник
|
||||||
|
</div>
|
||||||
|
<div className="persons__list__info">
|
||||||
|
В проекте - <span>“{projectBoard.name}”</span>
|
||||||
|
</div>
|
||||||
|
<div className="persons__list__items">
|
||||||
|
{projectBoard.projectUsers?.map((person) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="persons__list__item"
|
||||||
|
key={person.user_id}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className="avatar"
|
||||||
|
src={urlForLocal(person.user.avatar)}
|
||||||
|
alt="avatar"
|
||||||
|
/>
|
||||||
|
<span>{person.user.fio}</span>
|
||||||
|
<img
|
||||||
|
className="delete"
|
||||||
|
src={close}
|
||||||
|
alt="delete"
|
||||||
|
onClick={() => deletePerson(person.user_id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="persons__list__add"
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(modalToggle("addWorker"));
|
||||||
|
setModalAddWorker(true);
|
||||||
|
setPersonListOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="addPerson">+</span>
|
||||||
|
<p>Добавить участников</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
})
|
)}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
<div className='persons__list__add'
|
<div className="tasks__head__select">
|
||||||
onClick={() => {
|
<span>Учавствую</span>
|
||||||
dispatch(modalToggle("addWorker"));
|
<img src={selectArrow} alt="arrow" />
|
||||||
setModalAddWorker(true);
|
|
||||||
setPersonListOpen(false)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span className='addPerson'>+</span>
|
|
||||||
<p>Добавить участников</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="tasks__head__select">
|
||||||
}
|
<span>Мои</span>
|
||||||
</div>
|
<img src={selectArrow} alt="arrow" />
|
||||||
<div className="tasks__head__select">
|
</div>
|
||||||
<span>Учавствую</span>
|
<Link to={`/profile/tracker`} className="link">
|
||||||
<img src={selectArrow} alt="arrow" />
|
<div className="tasks__head__back">
|
||||||
</div>
|
<p>Вернуться на проекты</p>
|
||||||
<div className="tasks__head__select">
|
<img src={arrow} alt="arrow" />
|
||||||
<span>Мои</span>
|
|
||||||
<img src={selectArrow} alt="arrow" />
|
|
||||||
</div>
|
|
||||||
<Link to={`/profile/tracker`} className="link">
|
|
||||||
<div className="tasks__head__back">
|
|
||||||
<p>Вернуться на проекты</p>
|
|
||||||
<img src={arrow} alt="arrow" />
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="modal-tiket__content ticket">
|
|
||||||
<div className="content ticket-whith">
|
|
||||||
<div className="content__task">
|
|
||||||
<span>Задача</span>
|
|
||||||
{editOpen ? <input value={inputsValue.title} onChange={(e) => {
|
|
||||||
setInputsValue((prevValue) => ({...prevValue, title: e.target.value}))
|
|
||||||
}} /> :<h5>{inputsValue.title}</h5>}
|
|
||||||
<div className="content__description">
|
|
||||||
{editOpen ? <input value={inputsValue.description} onChange={(e) => {
|
|
||||||
setInputsValue((prevValue) => ({...prevValue, description: e.target.value}))
|
|
||||||
}}/> :<p>{inputsValue.description}</p>}
|
|
||||||
{/*<img src={task} className="image-task"></img>*/}
|
|
||||||
</div>
|
|
||||||
<div className="content__communication">
|
|
||||||
<p className="tasks">
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
dispatch(modalToggle("addSubtask"));
|
|
||||||
setModalAddWorker(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img src={plus}></img>
|
|
||||||
Добавить под задачу
|
|
||||||
</button>
|
|
||||||
</p>
|
|
||||||
<p className="file">
|
|
||||||
<button>
|
|
||||||
<img src={file}></img>
|
|
||||||
Загрузить файл
|
|
||||||
</button>
|
|
||||||
<span>{0}</span>
|
|
||||||
Файлов
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="content__input">
|
|
||||||
<input placeholder="Оставить комментарий" value={inputsValue.comment} onChange={(e) => {
|
|
||||||
setInputsValue((prevValue) => ({...prevValue, comment: e.target.value}))
|
|
||||||
}} />
|
|
||||||
<img src={send} onClick={createComment}></img>
|
|
||||||
</div>
|
|
||||||
<div className='comments__list'>
|
|
||||||
{comments.map((comment) => {
|
|
||||||
return <div className='comments__list__item' key={comment.id}>
|
|
||||||
<div className='comments__list__item__info'>
|
|
||||||
<span>{getCorrectDate(comment.created_at)}</span>
|
|
||||||
<div className={commentsEditOpen[comment.id] ? 'edit edit__open' : 'edit'} >
|
|
||||||
<img src={edit} alt='edit' onClick={() => {
|
|
||||||
if (commentsEditOpen[comment.id]) {
|
|
||||||
editComment(comment.id)
|
|
||||||
}
|
|
||||||
setCommentsEditOpen((prevValue) => ({...prevValue, [comment.id]: !prevValue[comment.id]}))
|
|
||||||
}} />
|
|
||||||
</div>
|
|
||||||
<img src={del} alt='delete' onClick={() => deleteComment(comment.id)} />
|
|
||||||
</div>
|
</div>
|
||||||
{commentsEditOpen[comment.id] ? <input value={commentsEditText[comment.id]} onChange={(e) => {
|
</Link>
|
||||||
setCommentsEditText((prevValue) => ({...prevValue, [comment.id]: e.target.value}))
|
</div>
|
||||||
}} /> : <p>{commentsEditText[comment.id]}</p>}
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="modal-tiket__content ticket">
|
||||||
|
<div className="content ticket-whith">
|
||||||
|
<div className="content__task">
|
||||||
|
<span>Задача</span>
|
||||||
|
{editOpen ? (
|
||||||
|
<input
|
||||||
|
value={inputsValue.title}
|
||||||
|
onChange={(e) => {
|
||||||
|
setInputsValue((prevValue) => ({
|
||||||
|
...prevValue,
|
||||||
|
title: e.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<h5>{inputsValue.title}</h5>
|
||||||
|
)}
|
||||||
|
<div className="content__description">
|
||||||
|
{editOpen ? (
|
||||||
|
<input
|
||||||
|
value={inputsValue.description}
|
||||||
|
onChange={(e) => {
|
||||||
|
setInputsValue((prevValue) => ({
|
||||||
|
...prevValue,
|
||||||
|
description: e.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<p>{inputsValue.description}</p>
|
||||||
|
)}
|
||||||
|
{/*<img src={task} className="image-task"></img>*/}
|
||||||
</div>
|
</div>
|
||||||
})
|
<div className="content__communication">
|
||||||
|
<p className="tasks">
|
||||||
}
|
<button
|
||||||
</div>
|
onClick={() => {
|
||||||
</div>
|
dispatch(modalToggle("addSubtask"));
|
||||||
</div>
|
setModalAddWorker(true);
|
||||||
<div className="workers">
|
}}
|
||||||
<div className="workers_box">
|
>
|
||||||
<p className="workers__creator">
|
<img src={plus}></img>
|
||||||
Создатель : <span>{taskInfo.user?.fio}</span>
|
Добавить под задачу
|
||||||
</p>
|
</button>
|
||||||
<div>
|
</p>
|
||||||
{Boolean(taskInfo.taskUsers?.length) &&
|
<p className="file">
|
||||||
taskInfo.taskUsers.map((worker, index) => {
|
<button>
|
||||||
return (
|
<img src={file}></img>
|
||||||
<div className="worker" key={index}>
|
Загрузить файл
|
||||||
<img src={worker.avatar}></img>
|
</button>
|
||||||
<p>{worker.name}</p>
|
<span>{0}</span>
|
||||||
</div>
|
Файлов
|
||||||
);
|
</p>
|
||||||
})}
|
</div>
|
||||||
|
<div className="content__input">
|
||||||
|
<input
|
||||||
|
placeholder="Оставить комментарий"
|
||||||
|
value={inputsValue.comment}
|
||||||
|
onChange={(e) => {
|
||||||
|
setInputsValue((prevValue) => ({
|
||||||
|
...prevValue,
|
||||||
|
comment: e.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<img src={send} onClick={createComment}></img>
|
||||||
|
</div>
|
||||||
|
<div className="comments__list">
|
||||||
|
{comments.map((comment) => {
|
||||||
|
return (
|
||||||
|
<div className="comments__list__item" key={comment.id}>
|
||||||
|
<div className="comments__list__item__info">
|
||||||
|
<span>{getCorrectDate(comment.created_at)}</span>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
commentsEditOpen[comment.id]
|
||||||
|
? "edit edit__open"
|
||||||
|
: "edit"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={edit}
|
||||||
|
alt="edit"
|
||||||
|
onClick={() => {
|
||||||
|
if (commentsEditOpen[comment.id]) {
|
||||||
|
editComment(comment.id);
|
||||||
|
}
|
||||||
|
setCommentsEditOpen((prevValue) => ({
|
||||||
|
...prevValue,
|
||||||
|
[comment.id]: !prevValue[comment.id],
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
src={del}
|
||||||
|
alt="delete"
|
||||||
|
onClick={() => deleteComment(comment.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{commentsEditOpen[comment.id] ? (
|
||||||
|
<input
|
||||||
|
value={commentsEditText[comment.id]}
|
||||||
|
onChange={(e) => {
|
||||||
|
setCommentsEditText((prevValue) => ({
|
||||||
|
...prevValue,
|
||||||
|
[comment.id]: e.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<p>{commentsEditText[comment.id]}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="workers">
|
||||||
|
<div className="workers_box">
|
||||||
|
<p className="workers__creator">
|
||||||
|
Создатель : <span>{taskInfo.user?.fio}</span>
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
{Boolean(taskInfo.taskUsers?.length) &&
|
||||||
|
taskInfo.taskUsers.map((worker, index) => {
|
||||||
|
return (
|
||||||
|
<div className="worker" key={index}>
|
||||||
|
<img src={worker.avatar}></img>
|
||||||
|
<p>{worker.name}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="add-worker moreItems">
|
<div className="add-worker moreItems">
|
||||||
<button
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(modalToggle("addWorker"));
|
||||||
|
setModalAddWorker(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
+
|
||||||
|
</button>
|
||||||
|
<span>Добавить исполнителя</span>
|
||||||
|
</div>
|
||||||
|
<div className="add-worker moreItems">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(modalToggle("addWorker"));
|
||||||
|
setModalAddWorker(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
+
|
||||||
|
</button>
|
||||||
|
<span>Добавить участников</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="workers_box-middle">
|
||||||
|
<div className="time">
|
||||||
|
<img src={watch}></img>
|
||||||
|
<span>Длительность : </span>
|
||||||
|
<p>{"0:00:00"}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button className="start">
|
||||||
|
Начать делать <img src={arrow2}></img>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="workers_box-bottom">
|
||||||
|
<div
|
||||||
|
className={editOpen ? "edit" : ""}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch(modalToggle("addWorker"));
|
if (editOpen) {
|
||||||
setModalAddWorker(true);
|
setEditOpen(!editOpen);
|
||||||
|
editTask();
|
||||||
|
} else {
|
||||||
|
setEditOpen(!editOpen);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
+
|
<img src={edit}></img>
|
||||||
</button>
|
<p>{editOpen ? "сохранить" : "редактировать"}</p>
|
||||||
<span>Добавить исполнителя</span>
|
</div>
|
||||||
</div>
|
<div>
|
||||||
<div className="add-worker moreItems">
|
<img src={link}></img>
|
||||||
<button
|
<p>ссылка на проект</p>
|
||||||
onClick={() => {
|
</div>
|
||||||
dispatch(modalToggle("addWorker"));
|
<div>
|
||||||
setModalAddWorker(true);
|
<img src={archive2}></img>
|
||||||
}}
|
<p>в архив</p>
|
||||||
>
|
</div>
|
||||||
+
|
<div onClick={deleteTask}>
|
||||||
</button>
|
<img src={del}></img>
|
||||||
<span>Добавить участников</span>
|
<p>удалить</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
<div className="workers_box-middle">
|
)}
|
||||||
<div className="time">
|
|
||||||
<img src={watch}></img>
|
|
||||||
<span>Длительность : </span>
|
|
||||||
<p>{"0:00:00"}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button className="start">
|
|
||||||
Начать делать <img src={arrow2}></img>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="workers_box-bottom">
|
|
||||||
<div className={editOpen ? 'edit' : ''} onClick={() => {
|
|
||||||
if(editOpen) {
|
|
||||||
setEditOpen(!editOpen)
|
|
||||||
editTask()
|
|
||||||
} else {
|
|
||||||
setEditOpen(!editOpen)
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
<img src={edit}></img>
|
|
||||||
<p>{editOpen ? 'сохранить' : 'редактировать'}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<img src={link}></img>
|
|
||||||
<p>ссылка на проект</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<img src={archive2}></img>
|
|
||||||
<p>в архив</p>
|
|
||||||
</div>
|
|
||||||
<div onClick={deleteTask}>
|
|
||||||
<img src={del}></img>
|
|
||||||
<p>удалить</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
</section>
|
</section>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React, {useEffect, useState} from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { apiRequest } from "../../../api/request";
|
import { apiRequest } from "../../../api/request";
|
||||||
import { urlForLocal } from '../../../helper'
|
import { urlForLocal } from "../../../helper";
|
||||||
import {
|
import {
|
||||||
setColumnName,
|
setColumnName,
|
||||||
getProjectBoard,
|
getProjectBoard,
|
||||||
@ -13,10 +13,10 @@ import {
|
|||||||
editColumnName,
|
editColumnName,
|
||||||
getColumnName,
|
getColumnName,
|
||||||
getColumnId,
|
getColumnId,
|
||||||
addPersonToProject
|
addPersonToProject,
|
||||||
} from "../../../redux/projectsTrackerSlice";
|
} from "../../../redux/projectsTrackerSlice";
|
||||||
|
|
||||||
import arrowDown from "../../../images/selectArrow.png"
|
import arrowDown from "../../../images/selectArrow.png";
|
||||||
|
|
||||||
import "./trackerModal.scss";
|
import "./trackerModal.scss";
|
||||||
|
|
||||||
@ -27,12 +27,12 @@ export const TrackerModal = ({
|
|||||||
defautlInput,
|
defautlInput,
|
||||||
titleProject,
|
titleProject,
|
||||||
projectId,
|
projectId,
|
||||||
priorityTask
|
priorityTask,
|
||||||
}) => {
|
}) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const projectBoard = useSelector(getProjectBoard);
|
const projectBoard = useSelector(getProjectBoard);
|
||||||
const columnName = useSelector(getColumnName);
|
const columnName = useSelector(getColumnName);
|
||||||
const columnId = useSelector(getColumnId)
|
const columnId = useSelector(getColumnId);
|
||||||
|
|
||||||
const modalType = useSelector(getValueModalType);
|
const modalType = useSelector(getValueModalType);
|
||||||
const [projectName, setProjectName] = useState(defautlInput);
|
const [projectName, setProjectName] = useState(defautlInput);
|
||||||
@ -40,9 +40,9 @@ export const TrackerModal = ({
|
|||||||
const [nameProject, setNameProject] = useState("");
|
const [nameProject, setNameProject] = useState("");
|
||||||
const [valueTiket, setValueTiket] = useState("");
|
const [valueTiket, setValueTiket] = useState("");
|
||||||
const [descriptionTicket, setDescriptionTicket] = useState("");
|
const [descriptionTicket, setDescriptionTicket] = useState("");
|
||||||
const [workers, setWorkers] = useState([])
|
const [workers, setWorkers] = useState([]);
|
||||||
const [selectWorkersOpen, setSelectWorkersOpen] = useState(false)
|
const [selectWorkersOpen, setSelectWorkersOpen] = useState(false);
|
||||||
const [selectedWorker, setSelectedWorker] = useState(null)
|
const [selectedWorker, setSelectedWorker] = useState(null);
|
||||||
|
|
||||||
function createTab() {
|
function createTab() {
|
||||||
if (!valueColumn) {
|
if (!valueColumn) {
|
||||||
@ -76,7 +76,7 @@ export const TrackerModal = ({
|
|||||||
status: 1,
|
status: 1,
|
||||||
user_id: localStorage.getItem("id"),
|
user_id: localStorage.getItem("id"),
|
||||||
column_id: selectedTab,
|
column_id: selectedTab,
|
||||||
priority: priorityTask
|
priority: priorityTask,
|
||||||
},
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
dispatch(setProjectBoardFetch(projectBoard.id));
|
dispatch(setProjectBoardFetch(projectBoard.id));
|
||||||
@ -105,12 +105,12 @@ export const TrackerModal = ({
|
|||||||
method: "PUT",
|
method: "PUT",
|
||||||
data: {
|
data: {
|
||||||
column_id: columnId,
|
column_id: columnId,
|
||||||
title: columnName
|
title: columnName,
|
||||||
}
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
setActive(false);
|
setActive(false);
|
||||||
dispatch(editColumnName({id: columnId, title: columnName}))
|
dispatch(editColumnName({ id: columnId, title: columnName }));
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createProject() {
|
function createProject() {
|
||||||
@ -138,34 +138,37 @@ export const TrackerModal = ({
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
data: {
|
data: {
|
||||||
user_id: selectedWorker.user_id,
|
user_id: selectedWorker.user_id,
|
||||||
project_id: projectBoard.id
|
project_id: projectBoard.id,
|
||||||
}
|
},
|
||||||
}).then((el) => {
|
}).then((el) => {
|
||||||
dispatch(addPersonToProject(el))
|
dispatch(addPersonToProject(el));
|
||||||
setActive(false);
|
setActive(false);
|
||||||
setSelectedWorker('')
|
setSelectedWorker("");
|
||||||
setSelectWorkersOpen(false)
|
setSelectWorkersOpen(false);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
modalType === "addWorker" ? apiRequest('/project/my-employee').then((el) => {
|
modalType === "addWorker"
|
||||||
let persons = el.managerEmployees
|
? apiRequest("/project/my-employee").then((el) => {
|
||||||
let ids = projectBoard.projectUsers.map((user) => user.user_id)
|
let persons = el.managerEmployees;
|
||||||
setWorkers(persons.reduce((acc, cur) => {
|
let ids = projectBoard.projectUsers.map((user) => user.user_id);
|
||||||
if (!ids.includes(cur.user_id)) acc.push(cur)
|
setWorkers(
|
||||||
return acc
|
persons.reduce((acc, cur) => {
|
||||||
}, []))
|
if (!ids.includes(cur.user_id)) acc.push(cur);
|
||||||
}) : ''
|
return acc;
|
||||||
}, [active])
|
}, [])
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: "";
|
||||||
|
}, [active]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={active ? "modal-add active" : "modal-add"}
|
className={active ? "modal-add active" : "modal-add"}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActive(false)
|
setActive(false);
|
||||||
setSelectWorkersOpen(false)
|
setSelectWorkersOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="modal-add__content" onClick={(e) => e.stopPropagation()}>
|
<div className="modal-add__content" onClick={(e) => e.stopPropagation()}>
|
||||||
@ -180,35 +183,49 @@ export const TrackerModal = ({
|
|||||||
{/* onChange={(e) => setEmailWorker(e.target.value)}*/}
|
{/* onChange={(e) => setEmailWorker(e.target.value)}*/}
|
||||||
{/* />*/}
|
{/* />*/}
|
||||||
{/*</div>*/}
|
{/*</div>*/}
|
||||||
<div className={selectWorkersOpen ? 'select__worker open' : 'select__worker'} onClick={() => setSelectWorkersOpen(!selectWorkersOpen)}>
|
<div
|
||||||
<p>{selectedWorker ? selectedWorker.employee.fio : 'Выберите пользователя'}</p>
|
className={
|
||||||
<img className='arrow' src={arrowDown} alt='arrow' />
|
selectWorkersOpen ? "select__worker open" : "select__worker"
|
||||||
{Boolean(selectWorkersOpen) &&
|
|
||||||
<div className='select__worker__dropDown'>
|
|
||||||
{Boolean(workers.length) ?
|
|
||||||
workers.map((worker) => {
|
|
||||||
if (worker === selectedWorker) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return <div className='worker' key={worker.id} onClick={() =>
|
|
||||||
{
|
|
||||||
setSelectedWorker(worker)
|
|
||||||
}
|
|
||||||
}>
|
|
||||||
<p>{worker.employee.fio}</p>
|
|
||||||
<img src={urlForLocal(worker.employee.avatar)} alt='avatar'/>
|
|
||||||
</div>
|
|
||||||
}) :
|
|
||||||
<div>Нет пользователей</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
onClick={() => setSelectWorkersOpen(!selectWorkersOpen)}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
{selectedWorker
|
||||||
|
? selectedWorker.employee.fio
|
||||||
|
: "Выберите пользователя"}
|
||||||
|
</p>
|
||||||
|
<img className="arrow" src={arrowDown} alt="arrow" />
|
||||||
|
{Boolean(selectWorkersOpen) && (
|
||||||
|
<div className="select__worker__dropDown">
|
||||||
|
{Boolean(workers.length) ? (
|
||||||
|
workers.map((worker) => {
|
||||||
|
if (worker === selectedWorker) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="worker"
|
||||||
|
key={worker.id}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedWorker(worker);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p>{worker.employee.fio}</p>
|
||||||
|
<img
|
||||||
|
src={urlForLocal(worker.employee.avatar)}
|
||||||
|
alt="avatar"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<div>Нет пользователей</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button className="button-add" onClick={addUserToProject}>
|
||||||
className="button-add"
|
|
||||||
onClick={addUserToProject}
|
|
||||||
>
|
|
||||||
Добавить
|
Добавить
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
Before Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 593 B |
6
src/images/category.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M4.75626 7.30423H3.39734C1.75305 7.30423 0.849365 6.40055 0.849365 4.75626V3.39734C0.849365 1.75305 1.75305 0.849365 3.39734 0.849365H4.75626C6.40055 0.849365 7.30423 1.75305 7.30423 3.39734V4.75626C7.30423 6.40055 6.40055 7.30423 4.75626 7.30423ZM3.39734 1.86855C2.32379 1.86855 1.86855 2.32379 1.86855 3.39734V4.75626C1.86855 5.8298 2.32379 6.28504 3.39734 6.28504H4.75626C5.8298 6.28504 6.28504 5.8298 6.28504 4.75626V3.39734C6.28504 2.32379 5.8298 1.86855 4.75626 1.86855H3.39734Z" fill="#787486"/>
|
||||||
|
<path d="M12.9098 7.30423H11.5508C9.90655 7.30423 9.00287 6.40055 9.00287 4.75626V3.39734C9.00287 1.75305 9.90655 0.849365 11.5508 0.849365H12.9098C14.5541 0.849365 15.4577 1.75305 15.4577 3.39734V4.75626C15.4577 6.40055 14.5541 7.30423 12.9098 7.30423ZM11.5508 1.86855C10.4773 1.86855 10.0221 2.32379 10.0221 3.39734V4.75626C10.0221 5.8298 10.4773 6.28504 11.5508 6.28504H12.9098C13.9833 6.28504 14.4385 5.8298 14.4385 4.75626V3.39734C14.4385 2.32379 13.9833 1.86855 12.9098 1.86855H11.5508Z" fill="#787486"/>
|
||||||
|
<path d="M12.9098 15.4577H11.5508C9.90655 15.4577 9.00287 14.554 9.00287 12.9097V11.5508C9.00287 9.90649 9.90655 9.00281 11.5508 9.00281H12.9098C14.5541 9.00281 15.4577 9.90649 15.4577 11.5508V12.9097C15.4577 14.554 14.5541 15.4577 12.9098 15.4577ZM11.5508 10.022C10.4773 10.022 10.0221 10.4772 10.0221 11.5508V12.9097C10.0221 13.9832 10.4773 14.4385 11.5508 14.4385H12.9098C13.9833 14.4385 14.4385 13.9832 14.4385 12.9097V11.5508C14.4385 10.4772 13.9833 10.022 12.9098 10.022H11.5508Z" fill="#787486"/>
|
||||||
|
<path d="M4.75626 15.4577H3.39734C1.75305 15.4577 0.849365 14.554 0.849365 12.9097V11.5508C0.849365 9.90649 1.75305 9.00281 3.39734 9.00281H4.75626C6.40055 9.00281 7.30423 9.90649 7.30423 11.5508V12.9097C7.30423 14.554 6.40055 15.4577 4.75626 15.4577ZM3.39734 10.022C2.32379 10.022 1.86855 10.4772 1.86855 11.5508V12.9097C1.86855 13.9832 2.32379 14.4385 3.39734 14.4385H4.75626C5.8298 14.4385 6.28504 13.9832 6.28504 12.9097V11.5508C6.28504 10.4772 5.8298 10.022 4.75626 10.022H3.39734Z" fill="#787486"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 268 B After Width: | Height: | Size: 268 B |
Before Width: | Height: | Size: 15 KiB |
8
src/images/remove.svg
Normal file
After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 475 B |
12
src/images/watch.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_323_3463)">
|
||||||
|
<path d="M14 7.00317C13.9868 10.8653 10.8389 14.0106 6.99629 14.0011C3.1283 13.9921 -0.0285369 10.8199 -6.48721e-06 6.97094C0.0290523 3.11563 3.16106 -0.00264003 7.00211 1.67724e-06C10.8532 0.00264339 14.0127 3.1653 13.9995 7.00317H14ZM0.802544 6.98996C0.786694 10.3957 3.55151 13.1768 6.97569 13.1996C10.3809 13.2223 13.1679 10.4617 13.1969 7.03699C13.2254 3.61914 10.4612 0.827385 7.02324 0.801496C3.61068 0.776136 0.818923 3.55363 0.803073 6.98943L0.802544 6.98996Z" fill="#DDE2E4"/>
|
||||||
|
<path d="M7.40101 4.01752C7.40101 4.51891 7.40471 5.02031 7.39837 5.5217C7.39678 5.66647 7.42003 5.75787 7.57748 5.82075C7.84693 5.92853 8.05087 6.13247 8.16129 6.40087C8.23156 6.57099 8.33195 6.60692 8.50049 6.60375C9.06317 6.59371 9.62639 6.60005 10.1896 6.60005C10.3227 6.60005 10.4569 6.6048 10.561 6.70202C10.6878 6.82037 10.7306 6.96619 10.6714 7.13315C10.6033 7.32599 10.4522 7.39943 10.2609 7.40049C9.6713 7.40313 9.08219 7.4063 8.49257 7.39943C8.33723 7.39785 8.24583 7.42532 8.17186 7.58858C7.93781 8.10371 7.38146 8.39219 6.83304 8.31558C6.24606 8.23315 5.7901 7.78829 5.69024 7.19972C5.59937 6.66345 5.87992 6.0865 6.38924 5.84294C6.56782 5.75734 6.60269 5.65749 6.60163 5.48261C6.59476 4.53265 6.59793 3.58216 6.59846 2.6322C6.59846 2.55295 6.59529 2.4737 6.60163 2.39498C6.62171 2.14137 6.76964 1.98815 6.99313 1.98446C7.22613 1.98023 7.39097 2.14085 7.39784 2.40819C7.40788 2.79493 7.40101 3.18221 7.40154 3.56948C7.40154 3.719 7.40154 3.86852 7.40154 4.01804L7.40101 4.01752ZM7.52623 6.99314C7.52147 6.70783 7.26153 6.46162 6.97887 6.4743C6.70254 6.48646 6.46796 6.73425 6.47271 7.00793C6.47747 7.28372 6.71839 7.52306 6.99419 7.52623C7.27896 7.5294 7.53151 7.27633 7.52676 6.99261L7.52623 6.99314Z" fill="#DDE2E4"/>
|
||||||
|
<path d="M7.52623 6.99314C7.53099 7.27686 7.27844 7.52993 6.99367 7.52676C6.71787 7.52359 6.47695 7.28373 6.47219 7.00846C6.46744 6.73425 6.70255 6.48699 6.97834 6.47483C7.26154 6.46215 7.52095 6.70836 7.52571 6.99367L7.52623 6.99314Z" fill="#FBFBFB"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_323_3463">
|
||||||
|
<rect width="14" height="14.0016" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
@ -8,7 +8,7 @@ import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileB
|
|||||||
import CardArticle from "../../components/UI/CardArticle/CardArticle";
|
import CardArticle from "../../components/UI/CardArticle/CardArticle";
|
||||||
|
|
||||||
import mockImgArticle from "../../images/mockImgArticle.png";
|
import mockImgArticle from "../../images/mockImgArticle.png";
|
||||||
import rightArrow from "../../images/right-arrow.png";
|
import rightArrow from "../../images/left-arrow.png";
|
||||||
import yandexZen from "../../images/yandexZen.svg";
|
import yandexZen from "../../images/yandexZen.svg";
|
||||||
import cardCalendar from "../../images/cardCalendar.svg";
|
import cardCalendar from "../../images/cardCalendar.svg";
|
||||||
import cardImg1 from "../../images/cardArticleItem.png";
|
import cardImg1 from "../../images/cardArticleItem.png";
|
||||||
|
@ -1,29 +1,31 @@
|
|||||||
import React, {useEffect} from 'react'
|
import React, { useEffect } from "react";
|
||||||
import {useDispatch, useSelector} from 'react-redux'
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import {useParams, useNavigate, Navigate} from 'react-router-dom'
|
import { useParams, useNavigate, Navigate } from "react-router-dom";
|
||||||
import SVG from 'react-inlinesvg'
|
import SVG from "react-inlinesvg";
|
||||||
|
|
||||||
import Form from '../../components/Form/Form'
|
import Form from "../../components/Form/Form";
|
||||||
import {Footer} from '../../components/Footer/Footer'
|
import { Footer } from "../../components/Footer/Footer";
|
||||||
import {LogoutButton} from "../../components/LogoutButton/LogoutButton";
|
import { LogoutButton } from "../../components/LogoutButton/LogoutButton";
|
||||||
|
|
||||||
|
import arrow from "../../images/left-arrow.png";
|
||||||
|
import rectangle from "../../images/rectangle_secondPage.png";
|
||||||
|
import telegramIcon from "../../images/telegram-icon.svg";
|
||||||
|
|
||||||
import arrow from '../../images/right-arrow.png'
|
import { LEVELS, SKILLS } from "../../constants/constants";
|
||||||
import rectangle from '../../images/rectangle_secondPage.png'
|
|
||||||
import telegramIcon from '../../images/telegram-icon.svg'
|
|
||||||
|
|
||||||
import {LEVELS, SKILLS} from '../../constants/constants'
|
import {
|
||||||
|
currentCandidate,
|
||||||
|
selectCurrentCandidate,
|
||||||
|
} from "../../redux/outstaffingSlice";
|
||||||
|
|
||||||
import {currentCandidate, selectCurrentCandidate} from '../../redux/outstaffingSlice'
|
import { apiRequest } from "../../api/request";
|
||||||
|
import { urlForLocal } from "../../helper";
|
||||||
|
|
||||||
import {apiRequest} from "../../api/request";
|
import "./formPage.scss";
|
||||||
import {urlForLocal} from "../../helper";
|
|
||||||
|
|
||||||
import './formPage.scss'
|
|
||||||
|
|
||||||
const FormPage = () => {
|
const FormPage = () => {
|
||||||
if(localStorage.getItem('role_status') !== '18') {
|
if (localStorage.getItem("role_status") !== "18") {
|
||||||
return <Navigate to="/profile" replace/>
|
return <Navigate to="/profile" replace />;
|
||||||
}
|
}
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -31,73 +33,73 @@ const FormPage = () => {
|
|||||||
const candidate = useSelector(selectCurrentCandidate);
|
const candidate = useSelector(selectCurrentCandidate);
|
||||||
|
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
navigate(-1)
|
navigate(-1);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(()=> {
|
useEffect(() => {
|
||||||
if (!candidate.id) {
|
if (!candidate.id) {
|
||||||
apiRequest('/profile', {
|
apiRequest("/profile", {
|
||||||
params: Number(params.id)
|
params: Number(params.id),
|
||||||
})
|
}).then((el) => dispatch(currentCandidate(el)));
|
||||||
.then((el) => dispatch(currentCandidate(el)))
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='container'>
|
<div className="container">
|
||||||
<div className='form-page'>
|
<div className="form-page">
|
||||||
<div className='form-page__back'>
|
<div className="form-page__back">
|
||||||
<div className='form-page__arrow' onClick={goBack}>
|
<div className="form-page__arrow" onClick={goBack}>
|
||||||
<div className='form-page__arrow-img'>
|
<div className="form-page__arrow-img">
|
||||||
<img src={arrow} alt=''/>
|
<img src={arrow} alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div className='form-page__back-to-candidate'>
|
<div className="form-page__back-to-candidate">
|
||||||
<span>Вернуться к кандидату</span>
|
<span>Вернуться к кандидату</span>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<LogoutButton />
|
|
||||||
</div>
|
</div>
|
||||||
<div className='form-page__candidate'>
|
<LogoutButton />
|
||||||
<div className='form-page__avatar'>
|
</div>
|
||||||
{candidate.photo && <img src={urlForLocal(candidate.photo)} alt='candidate avatar'/>}
|
<div className="form-page__candidate">
|
||||||
</div>
|
<div className="form-page__avatar">
|
||||||
<div className='form-page__candidate-info'>
|
{candidate.photo && (
|
||||||
<div className='form-page__position'>
|
<img src={urlForLocal(candidate.photo)} alt="candidate avatar" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="form-page__candidate-info">
|
||||||
|
<div className="form-page__position">
|
||||||
<span>
|
<span>
|
||||||
{candidate.specification} {SKILLS[candidate.position_id]},{' '}
|
{candidate.specification} {SKILLS[candidate.position_id]},{" "}
|
||||||
{LEVELS[candidate.level]}
|
{LEVELS[candidate.level]}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='form-page__selected'>
|
<div className="form-page__selected">
|
||||||
<img src={rectangle} alt='rectangle'/>
|
<img src={rectangle} alt="rectangle" />
|
||||||
<span>Выбранный кандидат</span>
|
<span>Выбранный кандидат</span>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='form-page__interview'>
|
|
||||||
<div className='form-page__form'>
|
|
||||||
<Form/>
|
|
||||||
</div>
|
|
||||||
<div className='form-page__separator'>
|
|
||||||
<div className='form-page__line'></div>
|
|
||||||
<div className='form-page__option'>или</div>
|
|
||||||
</div>
|
|
||||||
<div className='form-page__telegram'>
|
|
||||||
<div className='form-page__telegram-text'>
|
|
||||||
Заявка на собеседование через телеграм
|
|
||||||
</div>
|
|
||||||
<div className='form-page__telegram-icon'>
|
|
||||||
<a href='https://t.me/st0kir' target='_blank' rel="noreferrer">
|
|
||||||
<SVG src={telegramIcon}/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Footer/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="form-page__interview">
|
||||||
|
<div className="form-page__form">
|
||||||
|
<Form />
|
||||||
|
</div>
|
||||||
|
<div className="form-page__separator">
|
||||||
|
<div className="form-page__line"></div>
|
||||||
|
<div className="form-page__option">или</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-page__telegram">
|
||||||
|
<div className="form-page__telegram-text">
|
||||||
|
Заявка на собеседование через телеграм
|
||||||
|
</div>
|
||||||
|
<div className="form-page__telegram-icon">
|
||||||
|
<a href="https://t.me/st0kir" target="_blank" rel="noreferrer">
|
||||||
|
<SVG src={telegramIcon} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FormPage
|
export default FormPage;
|
||||||
|
@ -1,113 +1,112 @@
|
|||||||
import React from 'react'
|
import React from "react";
|
||||||
import arrowLeft from '../../images/right-arrow.png'
|
import { useNavigate } from "react-router";
|
||||||
|
import SVG from "react-inlinesvg";
|
||||||
|
|
||||||
import SVG from 'react-inlinesvg'
|
import { TaskItem } from "../../components/TaskItem/TaskItem";
|
||||||
|
import { LogoutButton } from "../../components/LogoutButton/LogoutButton";
|
||||||
|
|
||||||
import dateArrowIcon from '../../images/dateArrow.svg'
|
import arrowLeft from "../../images/left-arrow.png";
|
||||||
import calendarIcon from '../../images/calendar.svg'
|
import dateArrowIcon from "../../images/dateArrow.svg";
|
||||||
|
import calendarIcon from "../../images/calendar.svg";
|
||||||
|
|
||||||
import { TaskItem } from '../../components/TaskItem/TaskItem'
|
import "./singleReportPage.scss";
|
||||||
|
|
||||||
import './singleReportPage.scss'
|
|
||||||
import {useNavigate} from "react-router";
|
|
||||||
import {LogoutButton} from "../../components/LogoutButton/LogoutButton";
|
|
||||||
|
|
||||||
const tasks = [
|
const tasks = [
|
||||||
{
|
{
|
||||||
index: 1,
|
index: 1,
|
||||||
text: 'Задача «67 – Навигационная система – Главное меню – Обновить иконки» заблокирована из-за отсутствия новых иконок',
|
text: "Задача «67 – Навигационная система – Главное меню – Обновить иконки» заблокирована из-за отсутствия новых иконок",
|
||||||
hours: 3
|
hours: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
index: 2,
|
index: 2,
|
||||||
text: 'Задача «83 – Навигационная система – Поиск по почтовому индексу – Добавить экран поиска по почтовому индексу» не может быть завершена, т.к. работа над задачей «82 – Навигационная система – Разработать модуль поиска по почтовому индексу» ещё не начата',
|
text: "Задача «83 – Навигационная система – Поиск по почтовому индексу – Добавить экран поиска по почтовому индексу» не может быть завершена, т.к. работа над задачей «82 – Навигационная система – Разработать модуль поиска по почтовому индексу» ещё не начата",
|
||||||
hours: 3
|
hours: 3,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const SingleReportPage = () => {
|
const SingleReportPage = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
const navigate= useNavigate();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className="single-report-page">
|
||||||
<div className='single-report-page'>
|
<div
|
||||||
<div onClick={()=> {navigate(-1)}} className='single-report-page__back'>
|
onClick={() => {
|
||||||
<div className='single-report-page__back-arrow'>
|
navigate(-1);
|
||||||
<img src={arrowLeft} alt='arrowLeft'/>
|
}}
|
||||||
</div>
|
className="single-report-page__back"
|
||||||
<div className='single-report-page__back-text'>
|
>
|
||||||
Вернуться к списку
|
<div className="single-report-page__back-arrow">
|
||||||
</div>
|
<img src={arrowLeft} alt="arrowLeft" />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="single-report-page__back-text">Вернуться к списку</div>
|
||||||
<div className='single-report-page__title'>
|
|
||||||
<div className='single-report-page__title-text'>Отчет за день</div>
|
|
||||||
<div className='single-report-page__title-date'>
|
|
||||||
<div className='single-report-page__title-date--prev'>
|
|
||||||
<button>
|
|
||||||
<SVG src={dateArrowIcon} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className='single-report-page__title-date--actual'>
|
|
||||||
<SVG src={calendarIcon} />
|
|
||||||
<p>15 июня</p>
|
|
||||||
</div>
|
|
||||||
<div className='single-report-page__title-date--next single-report-page__title-date--enabled'>
|
|
||||||
<button>
|
|
||||||
<SVG src={dateArrowIcon} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='single-report-page__tasks'>
|
|
||||||
<div className='single-report-page__tasks-title'>
|
|
||||||
<div className='single-report-page__marker'></div>
|
|
||||||
<h3>Какие задачи были выполнены?</h3>
|
|
||||||
</div>
|
|
||||||
{tasks.map((task) => {
|
|
||||||
return (
|
|
||||||
<div className='single-report-page__tasks-item'>
|
|
||||||
<TaskItem {...task} />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='single-report-page__troubles'>
|
|
||||||
<div className='single-report-page__troubles-title'>
|
|
||||||
<div className='single-report-page__marker'></div>
|
|
||||||
<h3>Какие сложности возникли?</h3>
|
|
||||||
</div>
|
|
||||||
<div className='single-report-page__troubles-item'>
|
|
||||||
91 – Навигационная система – Поиск адреса – Разобраться, почему
|
|
||||||
находятся несколько пересечений Невского пр. и Казанской ул.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='single-report-page__scheduled'>
|
|
||||||
<div className='single-report-page__scheduled-title'>
|
|
||||||
<div className='single-report-page__marker'></div>
|
|
||||||
<h3>Что планируется сделать завтра?</h3>
|
|
||||||
</div>
|
|
||||||
<div className='single-report-page__scheduled-item'>
|
|
||||||
91 – Навигационная система – Поиск адреса – Разобраться, почему
|
|
||||||
находятся несколько пересечений Невского пр. и Казанской ул.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='single-report-page__footer'>
|
|
||||||
<div className='single-report-page__footer-rectangle'></div>
|
|
||||||
<div className='single-report-page__hours'>
|
|
||||||
<div className='single-report-page__hours-value'></div>
|
|
||||||
<div className='single-report-page__hours-text'></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<LogoutButton />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
)
|
<div className="single-report-page__title">
|
||||||
|
<div className="single-report-page__title-text">Отчет за день</div>
|
||||||
|
<div className="single-report-page__title-date">
|
||||||
|
<div className="single-report-page__title-date--prev">
|
||||||
|
<button>
|
||||||
|
<SVG src={dateArrowIcon} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="single-report-page__title-date--actual">
|
||||||
|
<SVG src={calendarIcon} />
|
||||||
|
<p>15 июня</p>
|
||||||
|
</div>
|
||||||
|
<div className="single-report-page__title-date--next single-report-page__title-date--enabled">
|
||||||
|
<button>
|
||||||
|
<SVG src={dateArrowIcon} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="single-report-page__tasks">
|
||||||
|
<div className="single-report-page__tasks-title">
|
||||||
|
<div className="single-report-page__marker"></div>
|
||||||
|
<h3>Какие задачи были выполнены?</h3>
|
||||||
|
</div>
|
||||||
|
{tasks.map((task) => {
|
||||||
|
return (
|
||||||
|
<div className="single-report-page__tasks-item">
|
||||||
|
<TaskItem {...task} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="single-report-page__troubles">
|
||||||
|
<div className="single-report-page__troubles-title">
|
||||||
|
<div className="single-report-page__marker"></div>
|
||||||
|
<h3>Какие сложности возникли?</h3>
|
||||||
|
</div>
|
||||||
|
<div className="single-report-page__troubles-item">
|
||||||
|
91 – Навигационная система – Поиск адреса – Разобраться, почему
|
||||||
|
находятся несколько пересечений Невского пр. и Казанской ул.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="single-report-page__scheduled">
|
||||||
|
<div className="single-report-page__scheduled-title">
|
||||||
|
<div className="single-report-page__marker"></div>
|
||||||
|
<h3>Что планируется сделать завтра?</h3>
|
||||||
|
</div>
|
||||||
|
<div className="single-report-page__scheduled-item">
|
||||||
|
91 – Навигационная система – Поиск адреса – Разобраться, почему
|
||||||
|
находятся несколько пересечений Невского пр. и Казанской ул.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="single-report-page__footer">
|
||||||
|
<div className="single-report-page__footer-rectangle"></div>
|
||||||
|
<div className="single-report-page__hours">
|
||||||
|
<div className="single-report-page__hours-value"></div>
|
||||||
|
<div className="single-report-page__hours-text"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<LogoutButton />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SingleReportPage
|
export default SingleReportPage;
|
||||||
|
@ -10,7 +10,7 @@ import { urlForLocal } from "../../helper";
|
|||||||
import { apiRequest } from "../../api/request";
|
import { apiRequest } from "../../api/request";
|
||||||
import { Navigation } from "../../components/Navigation/Navigation";
|
import { Navigation } from "../../components/Navigation/Navigation";
|
||||||
|
|
||||||
import arrow from "../../images/right-arrow.png";
|
import arrow from "../../images/left-arrow.png";
|
||||||
import rightArrow from "../../images/arrowRight.svg";
|
import rightArrow from "../../images/arrowRight.svg";
|
||||||
import gitImgItem from "../../images/gitItemImg.svg";
|
import gitImgItem from "../../images/gitItemImg.svg";
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
hourOfNum,
|
hourOfNum,
|
||||||
} from "../../components/Calendar/calendarHelper";
|
} from "../../components/Calendar/calendarHelper";
|
||||||
|
|
||||||
import arrow from "../../images/right-arrow.png";
|
import arrow from "../../images/left-arrow.png";
|
||||||
import arrowSwitchDate from "../../images/arrowViewReport.png";
|
import arrowSwitchDate from "../../images/arrowViewReport.png";
|
||||||
|
|
||||||
import "./viewReport.scss";
|
import "./viewReport.scss";
|
||||||
@ -103,7 +103,7 @@ export const ViewReport = () => {
|
|||||||
Ваши отчеты - <span>просмотр отчета за день</span>
|
Ваши отчеты - <span>просмотр отчета за день</span>
|
||||||
</h2>
|
</h2>
|
||||||
<Link className="viewReport__back" to={`/profile/calendar`}>
|
<Link className="viewReport__back" to={`/profile/calendar`}>
|
||||||
<img src={arrow} alt="arrow" />
|
<img src={arrow} alt="#" />
|
||||||
<p>Вернуться</p>
|
<p>Вернуться</p>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="viewReport__bar">
|
<div className="viewReport__bar">
|
||||||
|