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%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: 'LabGrotesque', sans-serif;
|
||||
font-family: "LabGrotesque", sans-serif;
|
||||
|
||||
&__head {
|
||||
background: #E1FCCF;
|
||||
background: #e1fccf;
|
||||
}
|
||||
|
||||
&__container {
|
||||
@ -26,7 +26,7 @@
|
||||
color: black;
|
||||
|
||||
span {
|
||||
color: #52B709;
|
||||
color: #52b709;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@ -45,7 +45,7 @@
|
||||
}
|
||||
|
||||
&__info {
|
||||
background: #FFFFFF;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
&__nav {
|
||||
|
@ -13,20 +13,19 @@ registerLocale("ru", ru);
|
||||
import { Loader } from "../Loader/Loader";
|
||||
import { Footer } from "../Footer/Footer";
|
||||
import { ProfileHeader } from "../ProfileHeader/ProfileHeader";
|
||||
import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs";
|
||||
import { ProfileBreadcrumbs } from "../ProfileBreadcrumbs/ProfileBreadcrumbs";
|
||||
|
||||
import { apiRequest } from "../../api/request";
|
||||
|
||||
import { Navigation } from "../Navigation/Navigation";
|
||||
import { getReportDate } from "../../redux/reportSlice";
|
||||
|
||||
import calendarIcon from "../../images/calendar.svg";
|
||||
import ellipse from "../../images/ellipse.png";
|
||||
import remove from "../../images/remove.png";
|
||||
import arrow from "../../images/right-arrow.png";
|
||||
import remove from "../../images/remove.svg";
|
||||
import arrow from "../../images/left-arrow.png";
|
||||
|
||||
import "./reportForm.scss";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import { Navigation } from "../Navigation/Navigation";
|
||||
|
||||
const ReportForm = () => {
|
||||
if (localStorage.getItem("role_status") === "18") {
|
@ -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 {
|
||||
@ -11,8 +13,6 @@ import {
|
||||
getSendRequest,
|
||||
setSendRequest,
|
||||
} from "../../redux/reportSlice";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Loader } from "../Loader/Loader";
|
||||
|
||||
import "./shortReport.scss";
|
||||
|
@ -1,12 +1,11 @@
|
||||
import React, { useState } from "react";
|
||||
import Slider from "react-slick";
|
||||
|
||||
import { Link } from "react-router-dom";
|
||||
import mockWorker from "../../images/mokPerson.png";
|
||||
|
||||
import "./sliderWorkers.scss";
|
||||
import "slick-carousel/slick/slick.css";
|
||||
import "slick-carousel/slick/slick-theme.css";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export const SliderWorkers = ({ title, titleInfo, subTitle }) => {
|
||||
const [workers] = useState([
|
||||
|
@ -1,44 +1,85 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import './StarRating.scss'
|
||||
import React, { useEffect, useState } from "react";
|
||||
import "./StarRating.scss";
|
||||
|
||||
const StarRating = ({ countStars=1, countActiveStars=1, color='#52B709', size=61 }) => {
|
||||
const [shadedStars, setShadedStars] = useState([])
|
||||
const [noShadedStars, setNoShadedStars] = useState([])
|
||||
const percent = (Math.abs(countActiveStars)) >= countStars ? 100 : (countActiveStars * 100 / countStars)
|
||||
useEffect(() => {
|
||||
for (let index = 0; index < countStars; index++) {
|
||||
setShadedStars(prev => [...prev, '★'])
|
||||
setNoShadedStars(prev => [...prev, '☆'])
|
||||
}
|
||||
}, [])
|
||||
const StarRating = ({
|
||||
countStars = 1,
|
||||
countActiveStars = 1,
|
||||
color = "#52B709",
|
||||
size = 61,
|
||||
}) => {
|
||||
const [shadedStars, setShadedStars] = useState([]);
|
||||
const [noShadedStars, setNoShadedStars] = useState([]);
|
||||
const percent =
|
||||
Math.abs(countActiveStars) >= countStars
|
||||
? 100
|
||||
: (countActiveStars * 100) / countStars;
|
||||
useEffect(() => {
|
||||
for (let index = 0; index < countStars; index++) {
|
||||
setShadedStars((prev) => [...prev, "★"]);
|
||||
setNoShadedStars((prev) => [...prev, "☆"]);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const ratingStyle = {
|
||||
"--size": size+'px'
|
||||
}
|
||||
const activeStyle = {
|
||||
"--width": percent + '%',
|
||||
"--color": color,
|
||||
"--content": shadedStars.join('')
|
||||
}
|
||||
const bodyStyle = {
|
||||
"--content": noShadedStars.join(''),
|
||||
"--color": color
|
||||
}
|
||||
const ratingStyle = {
|
||||
"--size": size + "px",
|
||||
};
|
||||
const activeStyle = {
|
||||
"--width": percent + "%",
|
||||
"--color": color,
|
||||
"--content": shadedStars.join(""),
|
||||
};
|
||||
const bodyStyle = {
|
||||
"--content": noShadedStars.join(""),
|
||||
"--color": color,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='rating' style={ratingStyle}>
|
||||
<div className="rating__body" style={bodyStyle} data-content={noShadedStars.join('')}>
|
||||
<div className="rating__active" style={activeStyle} data-content={shadedStars.join('')}></div>
|
||||
<div className="rating__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>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className="rating" style={ratingStyle}>
|
||||
<div
|
||||
className="rating__body"
|
||||
style={bodyStyle}
|
||||
data-content={noShadedStars.join("")}
|
||||
>
|
||||
<div
|
||||
className="rating__active"
|
||||
style={activeStyle}
|
||||
data-content={shadedStars.join("")}
|
||||
></div>
|
||||
<div className="rating__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);
|
@ -1,45 +1,44 @@
|
||||
.rating{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: var(--size);
|
||||
line-height: 0.75;
|
||||
&__body{
|
||||
position: relative;
|
||||
&::before{
|
||||
content: attr(data-content);
|
||||
display: block;
|
||||
color: var(--color);
|
||||
}
|
||||
}
|
||||
&__active{
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: var(--width);
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
&::before{
|
||||
content: attr(data-content);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: var(--color);
|
||||
}
|
||||
}
|
||||
&__items{
|
||||
display: flex;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
&__item{
|
||||
flex: 0 0 20%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
.rating {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: var(--size);
|
||||
line-height: 0.75;
|
||||
&__body {
|
||||
position: relative;
|
||||
&::before {
|
||||
content: attr(data-content);
|
||||
display: block;
|
||||
color: var(--color);
|
||||
}
|
||||
}
|
||||
&__active {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: var(--width);
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
&::before {
|
||||
content: attr(data-content);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: var(--color);
|
||||
}
|
||||
}
|
||||
&__items {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
&__item {
|
||||
flex: 0 0 20%;
|
||||
height: 100%;
|
||||
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;
|
||||
justify-content: space-between;
|
||||
top: -100px;
|
||||
padding:0 55px 0 85px;
|
||||
padding: 0 55px 0 85px;
|
||||
width: 100%;
|
||||
|
||||
@media (max-width: 965px) {
|
||||
@ -20,7 +20,7 @@
|
||||
font-weight: 900;
|
||||
font-size: 258px;
|
||||
line-height: 32px;
|
||||
color: #52B709;
|
||||
color: #52b709;
|
||||
}
|
||||
|
||||
p {
|
||||
@ -41,7 +41,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
p {
|
||||
background: #DDEEC6;
|
||||
background: #ddeec6;
|
||||
border-radius: 44px;
|
||||
padding: 8px 26px;
|
||||
font-weight: 400;
|
||||
|
@ -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 {
|
||||
margin-top: 0.2rem;
|
||||
color: #282828;
|
||||
font-family: 'GT Eesti Pro Display';
|
||||
font-family: "GT Eesti Pro Display";
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
line-height: 48.74px;
|
||||
@ -19,7 +19,7 @@
|
||||
max-width: 525px;
|
||||
margin-left: 1.6rem;
|
||||
color: #000000;
|
||||
font-family: 'GT Eesti Pro Display';
|
||||
font-family: "GT Eesti Pro Display";
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
letter-spacing: normal;
|
||||
@ -49,7 +49,7 @@
|
||||
);
|
||||
|
||||
color: #ffffff;
|
||||
font-family: 'Muller Extra Bold';
|
||||
font-family: "Muller Extra Bold";
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
text-align: left;
|
||||
@ -61,7 +61,7 @@
|
||||
width: 69px;
|
||||
height: 25px;
|
||||
color: #000000;
|
||||
font-family: 'GT Eesti Pro Display - Thin';
|
||||
font-family: "GT Eesti Pro Display - Thin";
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
letter-spacing: normal;
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React from "react";
|
||||
import cardCalendar from "../../../images/cardCalendar.svg";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import cardCalendar from "../../../images/cardCalendar.svg";
|
||||
|
||||
import "./cardArticle.scss";
|
||||
|
||||
export const CardArticle = ({ images, title, data, id }) => {
|
||||
|
@ -4,8 +4,8 @@ import { Link } from "react-router-dom";
|
||||
import ModalLayout from "../ModalLayout/ModalLayout";
|
||||
|
||||
import avatar from "../../../images/mokPerson.png";
|
||||
import logoTg from "../../../images/TgLogo.svg";
|
||||
import arrow from "../../../images/right-arrow.png";
|
||||
import logoTg from "../../../images/tgLogo.svg";
|
||||
import arrow from "../../../images/left-arrow.png";
|
||||
import interview from "../../../images/interviewLogo.svg";
|
||||
|
||||
import "./modalAspt.scss";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
import telegramLogo from "../../../images/TgLogo.svg";
|
||||
import telegramLogo from "../../../images/tgLogo.svg";
|
||||
import doc from "../../../images/doc.svg";
|
||||
import anyMoment from "../../../images/anyMoment.svg";
|
||||
|
||||
|
@ -1,17 +1,18 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import TrackerModal from "../TrackerModal/TrackerModal";
|
||||
import { apiRequest } from "../../../api/request";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { urlForLocal } from "../../../helper";
|
||||
import {
|
||||
modalToggle,
|
||||
setProjectBoardFetch,
|
||||
} from "../../../redux/projectsTrackerSlice";
|
||||
import { getCorrectDate } from "../../../components/Calendar/calendarHelper";
|
||||
|
||||
import {getCorrectDate} from '../../../components/Calendar/calendarHelper'
|
||||
|
||||
import category from "../../../images/category.png";
|
||||
import watch from "../../../images/watch.png";
|
||||
import category from "../../../images/category.svg";
|
||||
import watch from "../../../images/watch.svg";
|
||||
import file from "../../../images/fileModal.svg";
|
||||
import arrow from "../../../images/arrowStart.png";
|
||||
import link from "../../../images/link.svg";
|
||||
@ -24,7 +25,6 @@ import fullScreen from "../../../images/inFullScreen.svg";
|
||||
import close from "../../../images/closeProjectPersons.svg";
|
||||
|
||||
import "./ModalTicket.scss";
|
||||
import {urlForLocal} from "../../../helper";
|
||||
|
||||
export const ModalTiсket = ({
|
||||
active,
|
||||
@ -32,20 +32,24 @@ export const ModalTiсket = ({
|
||||
task,
|
||||
projectId,
|
||||
projectName,
|
||||
projectUsers
|
||||
projectUsers,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const [addSubtask, setAddSubtask] = useState(false);
|
||||
const [editOpen, setEditOpen] = useState(false);
|
||||
const [inputsValue, setInputsValue] = useState({title: task.title, description: task.description, comment: ''});
|
||||
const [inputsValue, setInputsValue] = useState({
|
||||
title: task.title,
|
||||
description: task.description,
|
||||
comment: "",
|
||||
});
|
||||
const [comments, setComments] = useState([]);
|
||||
const [commentsEditOpen, setCommentsEditOpen] = useState({})
|
||||
const [commentsEditText, setCommentsEditText] = useState({})
|
||||
const [dropListOpen, setDropListOpen] = useState(false)
|
||||
const [dropListMembersOpen, setDropListMembersOpen] = useState(false)
|
||||
const [executor, setExecutor] = useState(task.executor)
|
||||
const [members, setMembers] = useState(task.taskUsers)
|
||||
const [users, setUsers] = useState([])
|
||||
const [commentsEditOpen, setCommentsEditOpen] = useState({});
|
||||
const [commentsEditText, setCommentsEditText] = useState({});
|
||||
const [dropListOpen, setDropListOpen] = useState(false);
|
||||
const [dropListMembersOpen, setDropListMembersOpen] = useState(false);
|
||||
const [executor, setExecutor] = useState(task.executor);
|
||||
const [members, setMembers] = useState(task.taskUsers);
|
||||
const [users, setUsers] = useState([]);
|
||||
|
||||
function deleteTask() {
|
||||
apiRequest("/task/update-task", {
|
||||
@ -66,7 +70,7 @@ export const ModalTiсket = ({
|
||||
data: {
|
||||
task_id: task.id,
|
||||
title: inputsValue.title,
|
||||
description: inputsValue.description
|
||||
description: inputsValue.description,
|
||||
},
|
||||
}).then((res) => {
|
||||
dispatch(setProjectBoardFetch(projectId));
|
||||
@ -79,39 +83,42 @@ export const ModalTiсket = ({
|
||||
data: {
|
||||
text: inputsValue.comment,
|
||||
entity_type: 2,
|
||||
entity_id: task.id
|
||||
}
|
||||
entity_id: task.id,
|
||||
},
|
||||
}).then((res) => {
|
||||
let newComment = res
|
||||
newComment.created_at = new Date()
|
||||
setInputsValue((prevValue) => ({...prevValue, comment: ''}))
|
||||
setComments((prevValue) => ([...prevValue, newComment]))
|
||||
setCommentsEditOpen((prevValue) => ({...prevValue, [res.id]: false}))
|
||||
setCommentsEditText((prevValue) => ({...prevValue, [res.id]: res.text}))
|
||||
})
|
||||
let newComment = res;
|
||||
newComment.created_at = new Date();
|
||||
setInputsValue((prevValue) => ({ ...prevValue, comment: "" }));
|
||||
setComments((prevValue) => [...prevValue, newComment]);
|
||||
setCommentsEditOpen((prevValue) => ({ ...prevValue, [res.id]: false }));
|
||||
setCommentsEditText((prevValue) => ({
|
||||
...prevValue,
|
||||
[res.id]: res.text,
|
||||
}));
|
||||
});
|
||||
}
|
||||
function deleteComment(commentId) {
|
||||
apiRequest("/comment/update", {
|
||||
method: "PUT",
|
||||
data: {
|
||||
comment_id: commentId,
|
||||
status: 0
|
||||
}
|
||||
status: 0,
|
||||
},
|
||||
}).then((res) => {
|
||||
setComments((prevValue) => prevValue.filter((item) => item.id !== commentId))
|
||||
})
|
||||
setComments((prevValue) =>
|
||||
prevValue.filter((item) => item.id !== commentId)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function editComment(commentId) {
|
||||
|
||||
apiRequest("/comment/update", {
|
||||
method: "PUT",
|
||||
data: {
|
||||
comment_id: commentId,
|
||||
text: commentsEditText[commentId]
|
||||
}
|
||||
}).then((res) => {
|
||||
})
|
||||
text: commentsEditText[commentId],
|
||||
},
|
||||
}).then((res) => {});
|
||||
}
|
||||
|
||||
function taskExecutor(person) {
|
||||
@ -119,11 +126,11 @@ export const ModalTiсket = ({
|
||||
method: "PUT",
|
||||
data: {
|
||||
task_id: task.id,
|
||||
executor_id: person.user_id
|
||||
executor_id: person.user_id,
|
||||
},
|
||||
}).then((res) => {
|
||||
setDropListOpen(false)
|
||||
setExecutor(res.executor)
|
||||
setDropListOpen(false);
|
||||
setExecutor(res.executor);
|
||||
});
|
||||
}
|
||||
|
||||
@ -132,10 +139,10 @@ export const ModalTiсket = ({
|
||||
method: "PUT",
|
||||
data: {
|
||||
task_id: task.id,
|
||||
executor_id: 0
|
||||
executor_id: 0,
|
||||
},
|
||||
}).then((res) => {
|
||||
setExecutor(null)
|
||||
setExecutor(null);
|
||||
});
|
||||
}
|
||||
|
||||
@ -144,11 +151,11 @@ export const ModalTiсket = ({
|
||||
method: "POST",
|
||||
data: {
|
||||
task_id: task.id,
|
||||
user_id: person.user_id
|
||||
user_id: person.user_id,
|
||||
},
|
||||
}).then((res) => {
|
||||
setDropListMembersOpen(false)
|
||||
setMembers((prevValue) => ([...prevValue, res]))
|
||||
setDropListMembersOpen(false);
|
||||
setMembers((prevValue) => [...prevValue, res]);
|
||||
});
|
||||
}
|
||||
|
||||
@ -157,31 +164,40 @@ export const ModalTiсket = ({
|
||||
method: "DELETE",
|
||||
data: {
|
||||
task_id: task.id,
|
||||
user_id: person.user_id
|
||||
user_id: person.user_id,
|
||||
},
|
||||
}).then((res) => {
|
||||
setMembers(members.filter((item) => item.user_id !== person.user_id))
|
||||
setMembers(members.filter((item) => item.user_id !== person.user_id));
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
apiRequest(`/comment/get-by-entity?entity_type=2&entity_id=${task.id}`).then((res) => {
|
||||
setComments(res)
|
||||
apiRequest(
|
||||
`/comment/get-by-entity?entity_type=2&entity_id=${task.id}`
|
||||
).then((res) => {
|
||||
setComments(res);
|
||||
res.forEach((item) => {
|
||||
setCommentsEditOpen((prevValue) => ({...prevValue, [item.id]: false}))
|
||||
setCommentsEditText((prevValue) => ({...prevValue, [item.id]: item.text}))
|
||||
})
|
||||
})
|
||||
}, [])
|
||||
setCommentsEditOpen((prevValue) => ({
|
||||
...prevValue,
|
||||
[item.id]: false,
|
||||
}));
|
||||
setCommentsEditText((prevValue) => ({
|
||||
...prevValue,
|
||||
[item.id]: item.text,
|
||||
}));
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let ids = members.map((user) => user.user_id)
|
||||
setUsers(projectUsers.reduce((acc, cur) => {
|
||||
if (!ids.includes(cur.user_id)) acc.push(cur)
|
||||
return acc
|
||||
}, []))
|
||||
}, [members])
|
||||
|
||||
let ids = members.map((user) => user.user_id);
|
||||
setUsers(
|
||||
projectUsers.reduce((acc, cur) => {
|
||||
if (!ids.includes(cur.user_id)) acc.push(cur);
|
||||
return acc;
|
||||
}, [])
|
||||
);
|
||||
}, [members]);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -206,13 +222,33 @@ export const ModalTiсket = ({
|
||||
|
||||
<div className="content__task">
|
||||
<span>Задача</span>
|
||||
{editOpen ? <input value={inputsValue.title} onChange={(e) => {
|
||||
setInputsValue((prevValue) => ({...prevValue, title: e.target.value}))
|
||||
}} /> :<h5>{inputsValue.title}</h5>}
|
||||
{editOpen ? (
|
||||
<input
|
||||
value={inputsValue.title}
|
||||
onChange={(e) => {
|
||||
setInputsValue((prevValue) => ({
|
||||
...prevValue,
|
||||
title: e.target.value,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<h5>{inputsValue.title}</h5>
|
||||
)}
|
||||
<div className="content__description">
|
||||
{editOpen ? <input value={inputsValue.description} onChange={(e) => {
|
||||
setInputsValue((prevValue) => ({...prevValue, description: e.target.value}))
|
||||
}}/> :<p>{inputsValue.description}</p>}
|
||||
{editOpen ? (
|
||||
<input
|
||||
value={inputsValue.description}
|
||||
onChange={(e) => {
|
||||
setInputsValue((prevValue) => ({
|
||||
...prevValue,
|
||||
description: e.target.value,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<p>{inputsValue.description}</p>
|
||||
)}
|
||||
{/*<img src={taskImg} className="image-task"></img>*/}
|
||||
</div>
|
||||
<div className="content__communication">
|
||||
@ -237,97 +273,163 @@ export const ModalTiсket = ({
|
||||
</p>
|
||||
</div>
|
||||
<div className="content__input">
|
||||
<input placeholder="Оставить комментарий" value={inputsValue.comment} onChange={(e) => {
|
||||
setInputsValue((prevValue) => ({...prevValue, comment: e.target.value}))
|
||||
}} />
|
||||
<input
|
||||
placeholder="Оставить комментарий"
|
||||
value={inputsValue.comment}
|
||||
onChange={(e) => {
|
||||
setInputsValue((prevValue) => ({
|
||||
...prevValue,
|
||||
comment: e.target.value,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<img src={send} onClick={createComment}></img>
|
||||
</div>
|
||||
<div className='comments__list'>
|
||||
<div className="comments__list">
|
||||
{comments.map((comment) => {
|
||||
return <div className='comments__list__item' key={comment.id}>
|
||||
<div className='comments__list__item__info'>
|
||||
<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)
|
||||
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"
|
||||
}
|
||||
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>
|
||||
<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>
|
||||
{commentsEditOpen[comment.id] ? <input value={commentsEditText[comment.id]} onChange={(e) => {
|
||||
setCommentsEditText((prevValue) => ({...prevValue, [comment.id]: e.target.value}))
|
||||
}} /> : <p>{commentsEditText[comment.id]}</p>}
|
||||
</div>
|
||||
})
|
||||
|
||||
}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="workers">
|
||||
<div className="workers_box task__info">
|
||||
<span className="exit" onClick={() => setActive(false)}></span>
|
||||
<span className='nameProject'>{task.title}</span>
|
||||
<span className="nameProject">{task.title}</span>
|
||||
<p className="workers__creator">Создатель : {task.user?.fio}</p>
|
||||
|
||||
{executor ?
|
||||
<div className='executor'>
|
||||
{executor ? (
|
||||
<div className="executor">
|
||||
<p>Исполнитель: {executor.fio}</p>
|
||||
<img src={urlForLocal(executor.avatar)} alt='avatar' />
|
||||
<img src={close} className='delete' onClick={() => deleteTaskExecutor()} />
|
||||
</div> :
|
||||
<img src={urlForLocal(executor.avatar)} alt="avatar" />
|
||||
<img
|
||||
src={close}
|
||||
className="delete"
|
||||
onClick={() => deleteTaskExecutor()}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="add-worker moreItems ">
|
||||
<button onClick={() => setDropListOpen(true)}>+</button>
|
||||
<span>Добавить исполнителя</span>
|
||||
{dropListOpen &&
|
||||
<div className='dropdownList'>
|
||||
<img src={close} className='dropdownList__close' onClick={() => setDropListOpen(false)} />
|
||||
{dropListOpen && (
|
||||
<div className="dropdownList">
|
||||
<img
|
||||
src={close}
|
||||
className="dropdownList__close"
|
||||
onClick={() => setDropListOpen(false)}
|
||||
/>
|
||||
{projectUsers.map((person) => {
|
||||
return <div className='dropdownList__person' key={person.user_id} onClick={() => taskExecutor(person)}>
|
||||
<span>{person.user.fio}</span>
|
||||
<img src={urlForLocal(person.user.avatar)} />
|
||||
</div>
|
||||
})
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="dropdownList__person"
|
||||
key={person.user_id}
|
||||
onClick={() => taskExecutor(person)}
|
||||
>
|
||||
<span>{person.user.fio}</span>
|
||||
<img src={urlForLocal(person.user.avatar)} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
|
||||
{Boolean(members.length) &&
|
||||
<div className='members'>
|
||||
<p>Участники:</p>
|
||||
<div className='members__list'>
|
||||
{Boolean(members.length) && (
|
||||
<div className="members">
|
||||
<p>Участники:</p>
|
||||
<div className="members__list">
|
||||
{members.map((member) => {
|
||||
return <div className='worker' key={member.user_id}>
|
||||
<p>{member.fio}</p>
|
||||
<img src={urlForLocal(member.avatar)} />
|
||||
<img src={close} className='delete' onClick={() => deleteMember(member)} />
|
||||
</div>
|
||||
})
|
||||
}
|
||||
return (
|
||||
<div className="worker" key={member.user_id}>
|
||||
<p>{member.fio}</p>
|
||||
<img src={urlForLocal(member.avatar)} />
|
||||
<img
|
||||
src={close}
|
||||
className="delete"
|
||||
onClick={() => deleteMember(member)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="add-worker moreItems">
|
||||
<button onClick={() => setDropListMembersOpen(true)}>+</button>
|
||||
<span>Добавить участников</span>
|
||||
{dropListMembersOpen &&
|
||||
<div className='dropdownList'>
|
||||
<img src={close} className='dropdownList__close' onClick={() => setDropListMembersOpen(false)} />
|
||||
{users.length ? users.map((person) => {
|
||||
return <div className='dropdownList__person' key={person.user_id} onClick={() => addMember(person)}>
|
||||
<span>{person.user.fio}</span>
|
||||
<img src={urlForLocal(person.user.avatar)} />
|
||||
</div>
|
||||
}) : <p className='noUsers'>Нет пользователей</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
{dropListMembersOpen && (
|
||||
<div className="dropdownList">
|
||||
<img
|
||||
src={close}
|
||||
className="dropdownList__close"
|
||||
onClick={() => setDropListMembersOpen(false)}
|
||||
/>
|
||||
{users.length ? (
|
||||
users.map((person) => {
|
||||
return (
|
||||
<div
|
||||
className="dropdownList__person"
|
||||
key={person.user_id}
|
||||
onClick={() => addMember(person)}
|
||||
>
|
||||
<span>{person.user.fio}</span>
|
||||
<img src={urlForLocal(person.user.avatar)} />
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<p className="noUsers">Нет пользователей</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -344,16 +446,19 @@ export const ModalTiсket = ({
|
||||
</div>
|
||||
|
||||
<div className="workers_box-bottom">
|
||||
<div className={editOpen ? 'edit' : ''} onClick={() => {
|
||||
if(editOpen) {
|
||||
setEditOpen(!editOpen)
|
||||
editTask()
|
||||
} else {
|
||||
setEditOpen(!editOpen)
|
||||
}
|
||||
}}>
|
||||
<div
|
||||
className={editOpen ? "edit" : ""}
|
||||
onClick={() => {
|
||||
if (editOpen) {
|
||||
setEditOpen(!editOpen);
|
||||
editTask();
|
||||
} else {
|
||||
setEditOpen(!editOpen);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<img src={edit}></img>
|
||||
<p>{editOpen ? 'сохранить' : 'редактировать'}</p>
|
||||
<p>{editOpen ? "сохранить" : "редактировать"}</p>
|
||||
</div>
|
||||
<div>
|
||||
<img src={link}></img>
|
||||
|
@ -6,23 +6,24 @@ import { Footer } from "../../Footer/Footer";
|
||||
import { Link, useParams, useNavigate } from "react-router-dom";
|
||||
import TrackerModal from "../TrackerModal/TrackerModal";
|
||||
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 {
|
||||
deletePersonOnProject,
|
||||
modalToggle,
|
||||
setProjectBoardFetch,
|
||||
setToggleTab,
|
||||
getProjectBoard,
|
||||
getBoarderLoader
|
||||
getBoarderLoader,
|
||||
} from "../../../redux/projectsTrackerSlice";
|
||||
import { apiRequest } from "../../../api/request";
|
||||
import { urlForLocal } from "../../../helper";
|
||||
import { getCorrectDate } from "../../Calendar/calendarHelper";
|
||||
|
||||
import project from "../../../images/trackerProject.svg";
|
||||
import watch from "../../../images/watch.png";
|
||||
import watch from "../../../images/watch.svg";
|
||||
import file from "../../../images/fileModal.svg";
|
||||
import task from "../../../images/tasksMock.png";
|
||||
import send from "../../../images/send.svg";
|
||||
import arrow2 from "../../../images/arrowStart.png";
|
||||
import plus from "../../../images/plus.svg";
|
||||
@ -34,11 +35,9 @@ import link from "../../../images/link.svg";
|
||||
import archive2 from "../../../images/archive.svg";
|
||||
import del from "../../../images/delete.svg";
|
||||
import edit from "../../../images/edit.svg";
|
||||
import close from "../../../images/closeProjectPersons.svg";
|
||||
|
||||
import "./ticketFullScreen.scss";
|
||||
import close from "../../../images/closeProjectPersons.svg";
|
||||
import {urlForLocal} from "../../../helper";
|
||||
import {getCorrectDate} from "../../Calendar/calendarHelper";
|
||||
|
||||
export const TicketFullScreen = ({}) => {
|
||||
const [modalAddWorker, setModalAddWorker] = useState(false);
|
||||
@ -52,23 +51,35 @@ export const TicketFullScreen = ({}) => {
|
||||
const [inputsValue, setInputsValue] = useState({});
|
||||
const [loader, setLoader] = useState(true);
|
||||
const [comments, setComments] = useState([]);
|
||||
const [commentsEditOpen, setCommentsEditOpen] = useState({})
|
||||
const [commentsEditText, setCommentsEditText] = useState({})
|
||||
const [personListOpen, setPersonListOpen] = useState(false)
|
||||
const [commentsEditOpen, setCommentsEditOpen] = useState({});
|
||||
const [commentsEditText, setCommentsEditText] = useState({});
|
||||
const [personListOpen, setPersonListOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
apiRequest(`/task/get-task?task_id=${ticketId.id}`).then((taskInfo) => {
|
||||
setTaskInfo(taskInfo);
|
||||
setInputsValue({title: taskInfo.title, description: taskInfo.description, comment: ''})
|
||||
apiRequest(`/comment/get-by-entity?entity_type=2&entity_id=${taskInfo.id}`).then((res) => {
|
||||
setComments(res)
|
||||
setInputsValue({
|
||||
title: taskInfo.title,
|
||||
description: taskInfo.description,
|
||||
comment: "",
|
||||
});
|
||||
apiRequest(
|
||||
`/comment/get-by-entity?entity_type=2&entity_id=${taskInfo.id}`
|
||||
).then((res) => {
|
||||
setComments(res);
|
||||
res.forEach((item) => {
|
||||
setCommentsEditOpen((prevValue) => ({...prevValue, [item.id]: false}))
|
||||
setCommentsEditText((prevValue) => ({...prevValue, [item.id]: item.text}))
|
||||
})
|
||||
})
|
||||
setCommentsEditOpen((prevValue) => ({
|
||||
...prevValue,
|
||||
[item.id]: false,
|
||||
}));
|
||||
setCommentsEditText((prevValue) => ({
|
||||
...prevValue,
|
||||
[item.id]: item.text,
|
||||
}));
|
||||
});
|
||||
});
|
||||
dispatch(setProjectBoardFetch(taskInfo.project_id));
|
||||
setLoader(boardLoader)
|
||||
setLoader(boardLoader);
|
||||
});
|
||||
}, []);
|
||||
|
||||
@ -90,10 +101,9 @@ export const TicketFullScreen = ({}) => {
|
||||
data: {
|
||||
task_id: taskInfo.id,
|
||||
title: inputsValue.title,
|
||||
description: inputsValue.description
|
||||
description: inputsValue.description,
|
||||
},
|
||||
}).then((res) => {
|
||||
});
|
||||
}).then((res) => {});
|
||||
}
|
||||
|
||||
function createComment() {
|
||||
@ -102,16 +112,19 @@ export const TicketFullScreen = ({}) => {
|
||||
data: {
|
||||
text: inputsValue.comment,
|
||||
entity_type: 2,
|
||||
entity_id: taskInfo.id
|
||||
}
|
||||
entity_id: taskInfo.id,
|
||||
},
|
||||
}).then((res) => {
|
||||
let newComment = res
|
||||
newComment.created_at = new Date()
|
||||
setInputsValue((prevValue) => ({...prevValue, comment: ''}))
|
||||
setComments((prevValue) => ([...prevValue, newComment]))
|
||||
setCommentsEditOpen((prevValue) => ({...prevValue, [res.id]: false}))
|
||||
setCommentsEditText((prevValue) => ({...prevValue, [res.id]: res.text}))
|
||||
})
|
||||
let newComment = res;
|
||||
newComment.created_at = new Date();
|
||||
setInputsValue((prevValue) => ({ ...prevValue, comment: "" }));
|
||||
setComments((prevValue) => [...prevValue, newComment]);
|
||||
setCommentsEditOpen((prevValue) => ({ ...prevValue, [res.id]: false }));
|
||||
setCommentsEditText((prevValue) => ({
|
||||
...prevValue,
|
||||
[res.id]: res.text,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
function deleteComment(commentId) {
|
||||
@ -119,11 +132,13 @@ export const TicketFullScreen = ({}) => {
|
||||
method: "PUT",
|
||||
data: {
|
||||
comment_id: commentId,
|
||||
status: 0
|
||||
}
|
||||
status: 0,
|
||||
},
|
||||
}).then((res) => {
|
||||
setComments((prevValue) => prevValue.filter((item) => item.id !== commentId))
|
||||
})
|
||||
setComments((prevValue) =>
|
||||
prevValue.filter((item) => item.id !== commentId)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function editComment(commentId) {
|
||||
@ -131,10 +146,9 @@ export const TicketFullScreen = ({}) => {
|
||||
method: "PUT",
|
||||
data: {
|
||||
comment_id: commentId,
|
||||
text: commentsEditText[commentId]
|
||||
}
|
||||
}).then((res) => {
|
||||
})
|
||||
text: commentsEditText[commentId],
|
||||
},
|
||||
}).then((res) => {});
|
||||
}
|
||||
|
||||
function deletePerson(userId) {
|
||||
@ -142,10 +156,10 @@ export const TicketFullScreen = ({}) => {
|
||||
method: "DELETE",
|
||||
data: {
|
||||
project_id: projectBoard.id,
|
||||
user_id: userId
|
||||
user_id: userId,
|
||||
},
|
||||
}).then((res) => {
|
||||
dispatch(deletePersonOnProject(userId))
|
||||
dispatch(deletePersonOnProject(userId));
|
||||
});
|
||||
}
|
||||
|
||||
@ -195,223 +209,308 @@ export const TicketFullScreen = ({}) => {
|
||||
<p>Архив</p>
|
||||
</Link>
|
||||
</div>
|
||||
{loader ? <Loader /> :
|
||||
<>
|
||||
<div className="tracker__tabs__content content-tabs">
|
||||
<div className="tasks__head">
|
||||
<div className="tasks__head__wrapper">
|
||||
<h4>Проект : {projectBoard.name}</h4>
|
||||
{loader ? (
|
||||
<Loader />
|
||||
) : (
|
||||
<>
|
||||
<div className="tracker__tabs__content content-tabs">
|
||||
<div className="tasks__head">
|
||||
<div className="tasks__head__wrapper">
|
||||
<h4>Проект : {projectBoard.name}</h4>
|
||||
|
||||
<TrackerModal
|
||||
active={modalAddWorker}
|
||||
setActive={setModalAddWorker}
|
||||
></TrackerModal>
|
||||
<TrackerModal
|
||||
active={modalAddWorker}
|
||||
setActive={setModalAddWorker}
|
||||
></TrackerModal>
|
||||
|
||||
<div className="tasks__head__persons">
|
||||
{/*<img src={avatarTest} alt="avatar" />*/}
|
||||
{/*<img src={avatarTest} alt="avatar" />*/}
|
||||
<span className="countPersons">{projectBoard.projectUsers?.length}</span>
|
||||
<span
|
||||
className="addPerson"
|
||||
onClick={() => {
|
||||
setPersonListOpen(true)
|
||||
}}
|
||||
>
|
||||
+
|
||||
</span>
|
||||
<p>добавить участника</p>
|
||||
{personListOpen &&
|
||||
<div className='persons__list'>
|
||||
<img className='persons__list__close' src={close} alt='close' onClick={() => setPersonListOpen(false)} />
|
||||
<div className='persons__list__count'><span>{projectBoard.projectUsers?.length}</span>участник</div>
|
||||
<div className='persons__list__info'>В проекте - <span>“{projectBoard.name}”</span></div>
|
||||
<div className='persons__list__items'>
|
||||
{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 className="tasks__head__persons">
|
||||
{/*<img src={avatarTest} alt="avatar" />*/}
|
||||
{/*<img src={avatarTest} alt="avatar" />*/}
|
||||
<span className="countPersons">
|
||||
{projectBoard.projectUsers?.length}
|
||||
</span>
|
||||
<span
|
||||
className="addPerson"
|
||||
onClick={() => {
|
||||
setPersonListOpen(true);
|
||||
}}
|
||||
>
|
||||
+
|
||||
</span>
|
||||
<p>добавить участника</p>
|
||||
{personListOpen && (
|
||||
<div className="persons__list">
|
||||
<img
|
||||
className="persons__list__close"
|
||||
src={close}
|
||||
alt="close"
|
||||
onClick={() => setPersonListOpen(false)}
|
||||
/>
|
||||
<div className="persons__list__count">
|
||||
<span>{projectBoard.projectUsers?.length}</span>
|
||||
участник
|
||||
</div>
|
||||
<div className="persons__list__info">
|
||||
В проекте - <span>“{projectBoard.name}”</span>
|
||||
</div>
|
||||
<div className="persons__list__items">
|
||||
{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 className='persons__list__add'
|
||||
onClick={() => {
|
||||
dispatch(modalToggle("addWorker"));
|
||||
setModalAddWorker(true);
|
||||
setPersonListOpen(false)
|
||||
}}
|
||||
>
|
||||
<span className='addPerson'>+</span>
|
||||
<p>Добавить участников</p>
|
||||
<div className="tasks__head__select">
|
||||
<span>Учавствую</span>
|
||||
<img src={selectArrow} alt="arrow" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div className="tasks__head__select">
|
||||
<span>Учавствую</span>
|
||||
<img src={selectArrow} alt="arrow" />
|
||||
</div>
|
||||
<div className="tasks__head__select">
|
||||
<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 className="tasks__head__select">
|
||||
<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>
|
||||
{commentsEditOpen[comment.id] ? <input value={commentsEditText[comment.id]} onChange={(e) => {
|
||||
setCommentsEditText((prevValue) => ({...prevValue, [comment.id]: e.target.value}))
|
||||
}} /> : <p>{commentsEditText[comment.id]}</p>}
|
||||
</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>
|
||||
</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 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>
|
||||
{commentsEditOpen[comment.id] ? (
|
||||
<input
|
||||
value={commentsEditText[comment.id]}
|
||||
onChange={(e) => {
|
||||
setCommentsEditText((prevValue) => ({
|
||||
...prevValue,
|
||||
[comment.id]: e.target.value,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<p>{commentsEditText[comment.id]}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="workers">
|
||||
<div className="workers_box">
|
||||
<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">
|
||||
<button
|
||||
<div className="add-worker moreItems">
|
||||
<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={() => {
|
||||
dispatch(modalToggle("addWorker"));
|
||||
setModalAddWorker(true);
|
||||
if (editOpen) {
|
||||
setEditOpen(!editOpen);
|
||||
editTask();
|
||||
} else {
|
||||
setEditOpen(!editOpen);
|
||||
}
|
||||
}}
|
||||
>
|
||||
+
|
||||
</button>
|
||||
<span>Добавить исполнителя</span>
|
||||
</div>
|
||||
<div className="add-worker moreItems">
|
||||
<button
|
||||
onClick={() => {
|
||||
dispatch(modalToggle("addWorker"));
|
||||
setModalAddWorker(true);
|
||||
}}
|
||||
>
|
||||
+
|
||||
</button>
|
||||
<span>Добавить участников</span>
|
||||
>
|
||||
<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 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>
|
||||
<Footer />
|
||||
</section>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { apiRequest } from "../../../api/request";
|
||||
import { urlForLocal } from '../../../helper'
|
||||
import { urlForLocal } from "../../../helper";
|
||||
import {
|
||||
setColumnName,
|
||||
getProjectBoard,
|
||||
@ -13,10 +13,10 @@ import {
|
||||
editColumnName,
|
||||
getColumnName,
|
||||
getColumnId,
|
||||
addPersonToProject
|
||||
addPersonToProject,
|
||||
} from "../../../redux/projectsTrackerSlice";
|
||||
|
||||
import arrowDown from "../../../images/selectArrow.png"
|
||||
import arrowDown from "../../../images/selectArrow.png";
|
||||
|
||||
import "./trackerModal.scss";
|
||||
|
||||
@ -27,12 +27,12 @@ export const TrackerModal = ({
|
||||
defautlInput,
|
||||
titleProject,
|
||||
projectId,
|
||||
priorityTask
|
||||
priorityTask,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const projectBoard = useSelector(getProjectBoard);
|
||||
const columnName = useSelector(getColumnName);
|
||||
const columnId = useSelector(getColumnId)
|
||||
const columnId = useSelector(getColumnId);
|
||||
|
||||
const modalType = useSelector(getValueModalType);
|
||||
const [projectName, setProjectName] = useState(defautlInput);
|
||||
@ -40,9 +40,9 @@ export const TrackerModal = ({
|
||||
const [nameProject, setNameProject] = useState("");
|
||||
const [valueTiket, setValueTiket] = useState("");
|
||||
const [descriptionTicket, setDescriptionTicket] = useState("");
|
||||
const [workers, setWorkers] = useState([])
|
||||
const [selectWorkersOpen, setSelectWorkersOpen] = useState(false)
|
||||
const [selectedWorker, setSelectedWorker] = useState(null)
|
||||
const [workers, setWorkers] = useState([]);
|
||||
const [selectWorkersOpen, setSelectWorkersOpen] = useState(false);
|
||||
const [selectedWorker, setSelectedWorker] = useState(null);
|
||||
|
||||
function createTab() {
|
||||
if (!valueColumn) {
|
||||
@ -76,7 +76,7 @@ export const TrackerModal = ({
|
||||
status: 1,
|
||||
user_id: localStorage.getItem("id"),
|
||||
column_id: selectedTab,
|
||||
priority: priorityTask
|
||||
priority: priorityTask,
|
||||
},
|
||||
}).then((res) => {
|
||||
dispatch(setProjectBoardFetch(projectBoard.id));
|
||||
@ -105,12 +105,12 @@ export const TrackerModal = ({
|
||||
method: "PUT",
|
||||
data: {
|
||||
column_id: columnId,
|
||||
title: columnName
|
||||
}
|
||||
title: columnName,
|
||||
},
|
||||
}).then((res) => {
|
||||
setActive(false);
|
||||
dispatch(editColumnName({id: columnId, title: columnName}))
|
||||
})
|
||||
dispatch(editColumnName({ id: columnId, title: columnName }));
|
||||
});
|
||||
}
|
||||
|
||||
function createProject() {
|
||||
@ -138,34 +138,37 @@ export const TrackerModal = ({
|
||||
method: "POST",
|
||||
data: {
|
||||
user_id: selectedWorker.user_id,
|
||||
project_id: projectBoard.id
|
||||
}
|
||||
project_id: projectBoard.id,
|
||||
},
|
||||
}).then((el) => {
|
||||
dispatch(addPersonToProject(el))
|
||||
dispatch(addPersonToProject(el));
|
||||
setActive(false);
|
||||
setSelectedWorker('')
|
||||
setSelectWorkersOpen(false)
|
||||
})
|
||||
setSelectedWorker("");
|
||||
setSelectWorkersOpen(false);
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
modalType === "addWorker" ? apiRequest('/project/my-employee').then((el) => {
|
||||
let persons = el.managerEmployees
|
||||
let ids = projectBoard.projectUsers.map((user) => user.user_id)
|
||||
setWorkers(persons.reduce((acc, cur) => {
|
||||
if (!ids.includes(cur.user_id)) acc.push(cur)
|
||||
return acc
|
||||
}, []))
|
||||
}) : ''
|
||||
}, [active])
|
||||
|
||||
modalType === "addWorker"
|
||||
? apiRequest("/project/my-employee").then((el) => {
|
||||
let persons = el.managerEmployees;
|
||||
let ids = projectBoard.projectUsers.map((user) => user.user_id);
|
||||
setWorkers(
|
||||
persons.reduce((acc, cur) => {
|
||||
if (!ids.includes(cur.user_id)) acc.push(cur);
|
||||
return acc;
|
||||
}, [])
|
||||
);
|
||||
})
|
||||
: "";
|
||||
}, [active]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={active ? "modal-add active" : "modal-add"}
|
||||
onClick={() => {
|
||||
setActive(false)
|
||||
setSelectWorkersOpen(false)
|
||||
setActive(false);
|
||||
setSelectWorkersOpen(false);
|
||||
}}
|
||||
>
|
||||
<div className="modal-add__content" onClick={(e) => e.stopPropagation()}>
|
||||
@ -180,35 +183,49 @@ export const TrackerModal = ({
|
||||
{/* onChange={(e) => setEmailWorker(e.target.value)}*/}
|
||||
{/* />*/}
|
||||
{/*</div>*/}
|
||||
<div className={selectWorkersOpen ? 'select__worker open' : 'select__worker'} onClick={() => setSelectWorkersOpen(!selectWorkersOpen)}>
|
||||
<p>{selectedWorker ? selectedWorker.employee.fio : 'Выберите пользователя'}</p>
|
||||
<img className='arrow' src={arrowDown} alt='arrow' />
|
||||
{Boolean(selectWorkersOpen) &&
|
||||
<div className='select__worker__dropDown'>
|
||||
{Boolean(workers.length) ?
|
||||
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
|
||||
className={
|
||||
selectWorkersOpen ? "select__worker open" : "select__worker"
|
||||
}
|
||||
onClick={() => setSelectWorkersOpen(!selectWorkersOpen)}
|
||||
>
|
||||
<p>
|
||||
{selectedWorker
|
||||
? selectedWorker.employee.fio
|
||||
: "Выберите пользователя"}
|
||||
</p>
|
||||
<img className="arrow" src={arrowDown} alt="arrow" />
|
||||
{Boolean(selectWorkersOpen) && (
|
||||
<div className="select__worker__dropDown">
|
||||
{Boolean(workers.length) ? (
|
||||
workers.map((worker) => {
|
||||
if (worker === selectedWorker) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="worker"
|
||||
key={worker.id}
|
||||
onClick={() => {
|
||||
setSelectedWorker(worker);
|
||||
}}
|
||||
>
|
||||
<p>{worker.employee.fio}</p>
|
||||
<img
|
||||
src={urlForLocal(worker.employee.avatar)}
|
||||
alt="avatar"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div>Нет пользователей</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="button-add"
|
||||
onClick={addUserToProject}
|
||||
>
|
||||
<button className="button-add" onClick={addUserToProject}>
|
||||
Добавить
|
||||
</button>
|
||||
</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 mockImgArticle from "../../images/mockImgArticle.png";
|
||||
import rightArrow from "../../images/right-arrow.png";
|
||||
import rightArrow from "../../images/left-arrow.png";
|
||||
import yandexZen from "../../images/yandexZen.svg";
|
||||
import cardCalendar from "../../images/cardCalendar.svg";
|
||||
import cardImg1 from "../../images/cardArticleItem.png";
|
||||
|
@ -1,29 +1,31 @@
|
||||
import React, {useEffect} from 'react'
|
||||
import {useDispatch, useSelector} from 'react-redux'
|
||||
import {useParams, useNavigate, Navigate} from 'react-router-dom'
|
||||
import SVG from 'react-inlinesvg'
|
||||
import React, { useEffect } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useParams, useNavigate, Navigate } from "react-router-dom";
|
||||
import SVG from "react-inlinesvg";
|
||||
|
||||
import Form from '../../components/Form/Form'
|
||||
import {Footer} from '../../components/Footer/Footer'
|
||||
import {LogoutButton} from "../../components/LogoutButton/LogoutButton";
|
||||
import Form from "../../components/Form/Form";
|
||||
import { Footer } from "../../components/Footer/Footer";
|
||||
import { LogoutButton } from "../../components/LogoutButton/LogoutButton";
|
||||
|
||||
import arrow from "../../images/left-arrow.png";
|
||||
import rectangle from "../../images/rectangle_secondPage.png";
|
||||
import telegramIcon from "../../images/telegram-icon.svg";
|
||||
|
||||
import arrow from '../../images/right-arrow.png'
|
||||
import rectangle from '../../images/rectangle_secondPage.png'
|
||||
import telegramIcon from '../../images/telegram-icon.svg'
|
||||
import { LEVELS, SKILLS } from "../../constants/constants";
|
||||
|
||||
import {LEVELS, SKILLS} from '../../constants/constants'
|
||||
import {
|
||||
currentCandidate,
|
||||
selectCurrentCandidate,
|
||||
} from "../../redux/outstaffingSlice";
|
||||
|
||||
import {currentCandidate, selectCurrentCandidate} from '../../redux/outstaffingSlice'
|
||||
import { apiRequest } from "../../api/request";
|
||||
import { urlForLocal } from "../../helper";
|
||||
|
||||
import {apiRequest} from "../../api/request";
|
||||
import {urlForLocal} from "../../helper";
|
||||
|
||||
import './formPage.scss'
|
||||
import "./formPage.scss";
|
||||
|
||||
const FormPage = () => {
|
||||
if(localStorage.getItem('role_status') !== '18') {
|
||||
return <Navigate to="/profile" replace/>
|
||||
if (localStorage.getItem("role_status") !== "18") {
|
||||
return <Navigate to="/profile" replace />;
|
||||
}
|
||||
const params = useParams();
|
||||
const navigate = useNavigate();
|
||||
@ -31,73 +33,73 @@ const FormPage = () => {
|
||||
const candidate = useSelector(selectCurrentCandidate);
|
||||
|
||||
const goBack = () => {
|
||||
navigate(-1)
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
useEffect(()=> {
|
||||
useEffect(() => {
|
||||
if (!candidate.id) {
|
||||
apiRequest('/profile', {
|
||||
params: Number(params.id)
|
||||
})
|
||||
.then((el) => dispatch(currentCandidate(el)))
|
||||
apiRequest("/profile", {
|
||||
params: Number(params.id),
|
||||
}).then((el) => dispatch(currentCandidate(el)));
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
return (
|
||||
<div className='container'>
|
||||
<div className='form-page'>
|
||||
<div className='form-page__back'>
|
||||
<div className='form-page__arrow' onClick={goBack}>
|
||||
<div className='form-page__arrow-img'>
|
||||
<img src={arrow} alt=''/>
|
||||
</div>
|
||||
<div className='form-page__back-to-candidate'>
|
||||
<span>Вернуться к кандидату</span>
|
||||
</div>
|
||||
<div className="container">
|
||||
<div className="form-page">
|
||||
<div className="form-page__back">
|
||||
<div className="form-page__arrow" onClick={goBack}>
|
||||
<div className="form-page__arrow-img">
|
||||
<img src={arrow} alt="" />
|
||||
</div>
|
||||
<div className="form-page__back-to-candidate">
|
||||
<span>Вернуться к кандидату</span>
|
||||
</div>
|
||||
<LogoutButton />
|
||||
</div>
|
||||
<div className='form-page__candidate'>
|
||||
<div className='form-page__avatar'>
|
||||
{candidate.photo && <img src={urlForLocal(candidate.photo)} alt='candidate avatar'/>}
|
||||
</div>
|
||||
<div className='form-page__candidate-info'>
|
||||
<div className='form-page__position'>
|
||||
<LogoutButton />
|
||||
</div>
|
||||
<div className="form-page__candidate">
|
||||
<div className="form-page__avatar">
|
||||
{candidate.photo && (
|
||||
<img src={urlForLocal(candidate.photo)} alt="candidate avatar" />
|
||||
)}
|
||||
</div>
|
||||
<div className="form-page__candidate-info">
|
||||
<div className="form-page__position">
|
||||
<span>
|
||||
{candidate.specification} {SKILLS[candidate.position_id]},{' '}
|
||||
{candidate.specification} {SKILLS[candidate.position_id]},{" "}
|
||||
{LEVELS[candidate.level]}
|
||||
</span>
|
||||
</div>
|
||||
<div className='form-page__selected'>
|
||||
<img src={rectangle} alt='rectangle'/>
|
||||
<span>Выбранный кандидат</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-page__selected">
|
||||
<img src={rectangle} alt="rectangle" />
|
||||
<span>Выбранный кандидат</span>
|
||||
</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 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>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default FormPage
|
||||
export default FormPage;
|
||||
|
@ -1,113 +1,112 @@
|
||||
import React from 'react'
|
||||
import arrowLeft from '../../images/right-arrow.png'
|
||||
import React from "react";
|
||||
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 calendarIcon from '../../images/calendar.svg'
|
||||
import arrowLeft from "../../images/left-arrow.png";
|
||||
import dateArrowIcon from "../../images/dateArrow.svg";
|
||||
import calendarIcon from "../../images/calendar.svg";
|
||||
|
||||
import { TaskItem } from '../../components/TaskItem/TaskItem'
|
||||
|
||||
import './singleReportPage.scss'
|
||||
import {useNavigate} from "react-router";
|
||||
import {LogoutButton} from "../../components/LogoutButton/LogoutButton";
|
||||
import "./singleReportPage.scss";
|
||||
|
||||
const tasks = [
|
||||
{
|
||||
index: 1,
|
||||
text: 'Задача «67 – Навигационная система – Главное меню – Обновить иконки» заблокирована из-за отсутствия новых иконок',
|
||||
hours: 3
|
||||
text: "Задача «67 – Навигационная система – Главное меню – Обновить иконки» заблокирована из-за отсутствия новых иконок",
|
||||
hours: 3,
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
text: 'Задача «83 – Навигационная система – Поиск по почтовому индексу – Добавить экран поиска по почтовому индексу» не может быть завершена, т.к. работа над задачей «82 – Навигационная система – Разработать модуль поиска по почтовому индексу» ещё не начата',
|
||||
hours: 3
|
||||
}
|
||||
text: "Задача «83 – Навигационная система – Поиск по почтовому индексу – Добавить экран поиска по почтовому индексу» не может быть завершена, т.к. работа над задачей «82 – Навигационная система – Разработать модуль поиска по почтовому индексу» ещё не начата",
|
||||
hours: 3,
|
||||
},
|
||||
];
|
||||
|
||||
const SingleReportPage = () => {
|
||||
|
||||
const navigate= useNavigate();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
|
||||
<div className='single-report-page'>
|
||||
<div onClick={()=> {navigate(-1)}} className='single-report-page__back'>
|
||||
<div className='single-report-page__back-arrow'>
|
||||
<img src={arrowLeft} alt='arrowLeft'/>
|
||||
</div>
|
||||
<div className='single-report-page__back-text'>
|
||||
Вернуться к списку
|
||||
</div>
|
||||
<div className="single-report-page">
|
||||
<div
|
||||
onClick={() => {
|
||||
navigate(-1);
|
||||
}}
|
||||
className="single-report-page__back"
|
||||
>
|
||||
<div className="single-report-page__back-arrow">
|
||||
<img src={arrowLeft} alt="arrowLeft" />
|
||||
</div>
|
||||
|
||||
<div className='single-report-page__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 className="single-report-page__back-text">Вернуться к списку</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
<div className="single-report-page__title">
|
||||
<div className="single-report-page__title-text">Отчет за день</div>
|
||||
<div className="single-report-page__title-date">
|
||||
<div className="single-report-page__title-date--prev">
|
||||
<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 { Navigation } from "../../components/Navigation/Navigation";
|
||||
|
||||
import arrow from "../../images/right-arrow.png";
|
||||
import arrow from "../../images/left-arrow.png";
|
||||
import rightArrow from "../../images/arrowRight.svg";
|
||||
import gitImgItem from "../../images/gitItemImg.svg";
|
||||
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
hourOfNum,
|
||||
} from "../../components/Calendar/calendarHelper";
|
||||
|
||||
import arrow from "../../images/right-arrow.png";
|
||||
import arrow from "../../images/left-arrow.png";
|
||||
import arrowSwitchDate from "../../images/arrowViewReport.png";
|
||||
|
||||
import "./viewReport.scss";
|
||||
@ -103,7 +103,7 @@ export const ViewReport = () => {
|
||||
Ваши отчеты - <span>просмотр отчета за день</span>
|
||||
</h2>
|
||||
<Link className="viewReport__back" to={`/profile/calendar`}>
|
||||
<img src={arrow} alt="arrow" />
|
||||
<img src={arrow} alt="#" />
|
||||
<p>Вернуться</p>
|
||||
</Link>
|
||||
<div className="viewReport__bar">
|
||||
|