Fixed full components

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,45 +1,44 @@
.rating{ .rating {
display: flex; display: flex;
align-items: center; align-items: center;
font-size: var(--size); font-size: var(--size);
line-height: 0.75; line-height: 0.75;
&__body{ &__body {
position: relative; position: relative;
&::before{ &::before {
content: attr(data-content); content: attr(data-content);
display: block; display: block;
color: var(--color); color: var(--color);
} }
} }
&__active{ &__active {
position: absolute; position: absolute;
height: 100%; height: 100%;
width: var(--width); width: var(--width);
top: 0; top: 0;
left: 0; left: 0;
overflow: hidden; overflow: hidden;
&::before{ &::before {
content: attr(data-content); content: attr(data-content);
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
color: var(--color); color: var(--color);
} }
} }
&__items{ &__items {
display: flex; display: flex;
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
left: 0; left: 0;
top: 0; top: 0;
} }
&__item{ &__item {
flex: 0 0 20%; flex: 0 0 20%;
height: 100%; height: 100%;
opacity: 0; opacity: 0;
} }
} }

View File

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

View File

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

View File

@ -4,7 +4,7 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
top: -100px; top: -100px;
padding:0 55px 0 85px; padding: 0 55px 0 85px;
width: 100%; width: 100%;
@media (max-width: 965px) { @media (max-width: 965px) {
@ -20,7 +20,7 @@
font-weight: 900; font-weight: 900;
font-size: 258px; font-size: 258px;
line-height: 32px; line-height: 32px;
color: #52B709; color: #52b709;
} }
p { p {
@ -41,7 +41,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
p { p {
background: #DDEEC6; background: #ddeec6;
border-radius: 44px; border-radius: 44px;
padding: 8px 26px; padding: 8px 26px;
font-weight: 400; font-weight: 400;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,23 +6,24 @@ import { Footer } from "../../Footer/Footer";
import { Link, useParams, useNavigate } from "react-router-dom"; import { Link, useParams, useNavigate } from "react-router-dom";
import TrackerModal from "../TrackerModal/TrackerModal"; import TrackerModal from "../TrackerModal/TrackerModal";
import { Navigation } from "../../Navigation/Navigation"; import { Navigation } from "../../Navigation/Navigation";
import {Loader} from "../../Loader/Loader"; import { Loader } from "../../Loader/Loader";
import {useDispatch, useSelector} from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { import {
deletePersonOnProject, deletePersonOnProject,
modalToggle, modalToggle,
setProjectBoardFetch, setProjectBoardFetch,
setToggleTab, setToggleTab,
getProjectBoard, getProjectBoard,
getBoarderLoader getBoarderLoader,
} from "../../../redux/projectsTrackerSlice"; } from "../../../redux/projectsTrackerSlice";
import { apiRequest } from "../../../api/request"; import { apiRequest } from "../../../api/request";
import { urlForLocal } from "../../../helper";
import { getCorrectDate } from "../../Calendar/calendarHelper";
import project from "../../../images/trackerProject.svg"; import project from "../../../images/trackerProject.svg";
import watch from "../../../images/watch.png"; import watch from "../../../images/watch.svg";
import file from "../../../images/fileModal.svg"; import file from "../../../images/fileModal.svg";
import task from "../../../images/tasksMock.png";
import send from "../../../images/send.svg"; import send from "../../../images/send.svg";
import arrow2 from "../../../images/arrowStart.png"; import arrow2 from "../../../images/arrowStart.png";
import plus from "../../../images/plus.svg"; import plus from "../../../images/plus.svg";
@ -34,11 +35,9 @@ import link from "../../../images/link.svg";
import archive2 from "../../../images/archive.svg"; import archive2 from "../../../images/archive.svg";
import del from "../../../images/delete.svg"; import del from "../../../images/delete.svg";
import edit from "../../../images/edit.svg"; import edit from "../../../images/edit.svg";
import close from "../../../images/closeProjectPersons.svg";
import "./ticketFullScreen.scss"; import "./ticketFullScreen.scss";
import close from "../../../images/closeProjectPersons.svg";
import {urlForLocal} from "../../../helper";
import {getCorrectDate} from "../../Calendar/calendarHelper";
export const TicketFullScreen = ({}) => { export const TicketFullScreen = ({}) => {
const [modalAddWorker, setModalAddWorker] = useState(false); const [modalAddWorker, setModalAddWorker] = useState(false);
@ -52,23 +51,35 @@ export const TicketFullScreen = ({}) => {
const [inputsValue, setInputsValue] = useState({}); const [inputsValue, setInputsValue] = useState({});
const [loader, setLoader] = useState(true); const [loader, setLoader] = useState(true);
const [comments, setComments] = useState([]); const [comments, setComments] = useState([]);
const [commentsEditOpen, setCommentsEditOpen] = useState({}) const [commentsEditOpen, setCommentsEditOpen] = useState({});
const [commentsEditText, setCommentsEditText] = useState({}) const [commentsEditText, setCommentsEditText] = useState({});
const [personListOpen, setPersonListOpen] = useState(false) const [personListOpen, setPersonListOpen] = useState(false);
useEffect(() => { useEffect(() => {
apiRequest(`/task/get-task?task_id=${ticketId.id}`).then((taskInfo) => { apiRequest(`/task/get-task?task_id=${ticketId.id}`).then((taskInfo) => {
setTaskInfo(taskInfo); setTaskInfo(taskInfo);
setInputsValue({title: taskInfo.title, description: taskInfo.description, comment: ''}) setInputsValue({
apiRequest(`/comment/get-by-entity?entity_type=2&entity_id=${taskInfo.id}`).then((res) => { title: taskInfo.title,
setComments(res) description: taskInfo.description,
comment: "",
});
apiRequest(
`/comment/get-by-entity?entity_type=2&entity_id=${taskInfo.id}`
).then((res) => {
setComments(res);
res.forEach((item) => { res.forEach((item) => {
setCommentsEditOpen((prevValue) => ({...prevValue, [item.id]: false})) setCommentsEditOpen((prevValue) => ({
setCommentsEditText((prevValue) => ({...prevValue, [item.id]: item.text})) ...prevValue,
}) [item.id]: false,
}) }));
setCommentsEditText((prevValue) => ({
...prevValue,
[item.id]: item.text,
}));
});
});
dispatch(setProjectBoardFetch(taskInfo.project_id)); dispatch(setProjectBoardFetch(taskInfo.project_id));
setLoader(boardLoader) setLoader(boardLoader);
}); });
}, []); }, []);
@ -90,10 +101,9 @@ export const TicketFullScreen = ({}) => {
data: { data: {
task_id: taskInfo.id, task_id: taskInfo.id,
title: inputsValue.title, title: inputsValue.title,
description: inputsValue.description description: inputsValue.description,
}, },
}).then((res) => { }).then((res) => {});
});
} }
function createComment() { function createComment() {
@ -102,16 +112,19 @@ export const TicketFullScreen = ({}) => {
data: { data: {
text: inputsValue.comment, text: inputsValue.comment,
entity_type: 2, entity_type: 2,
entity_id: taskInfo.id entity_id: taskInfo.id,
} },
}).then((res) => { }).then((res) => {
let newComment = res let newComment = res;
newComment.created_at = new Date() newComment.created_at = new Date();
setInputsValue((prevValue) => ({...prevValue, comment: ''})) setInputsValue((prevValue) => ({ ...prevValue, comment: "" }));
setComments((prevValue) => ([...prevValue, newComment])) setComments((prevValue) => [...prevValue, newComment]);
setCommentsEditOpen((prevValue) => ({...prevValue, [res.id]: false})) setCommentsEditOpen((prevValue) => ({ ...prevValue, [res.id]: false }));
setCommentsEditText((prevValue) => ({...prevValue, [res.id]: res.text})) setCommentsEditText((prevValue) => ({
}) ...prevValue,
[res.id]: res.text,
}));
});
} }
function deleteComment(commentId) { function deleteComment(commentId) {
@ -119,11 +132,13 @@ export const TicketFullScreen = ({}) => {
method: "PUT", method: "PUT",
data: { data: {
comment_id: commentId, comment_id: commentId,
status: 0 status: 0,
} },
}).then((res) => { }).then((res) => {
setComments((prevValue) => prevValue.filter((item) => item.id !== commentId)) setComments((prevValue) =>
}) prevValue.filter((item) => item.id !== commentId)
);
});
} }
function editComment(commentId) { function editComment(commentId) {
@ -131,10 +146,9 @@ export const TicketFullScreen = ({}) => {
method: "PUT", method: "PUT",
data: { data: {
comment_id: commentId, comment_id: commentId,
text: commentsEditText[commentId] text: commentsEditText[commentId],
} },
}).then((res) => { }).then((res) => {});
})
} }
function deletePerson(userId) { function deletePerson(userId) {
@ -142,10 +156,10 @@ export const TicketFullScreen = ({}) => {
method: "DELETE", method: "DELETE",
data: { data: {
project_id: projectBoard.id, project_id: projectBoard.id,
user_id: userId user_id: userId,
}, },
}).then((res) => { }).then((res) => {
dispatch(deletePersonOnProject(userId)) dispatch(deletePersonOnProject(userId));
}); });
} }
@ -195,223 +209,308 @@ export const TicketFullScreen = ({}) => {
<p>Архив</p> <p>Архив</p>
</Link> </Link>
</div> </div>
{loader ? <Loader /> : {loader ? (
<> <Loader />
<div className="tracker__tabs__content content-tabs"> ) : (
<div className="tasks__head"> <>
<div className="tasks__head__wrapper"> <div className="tracker__tabs__content content-tabs">
<h4>Проект : {projectBoard.name}</h4> <div className="tasks__head">
<div className="tasks__head__wrapper">
<h4>Проект : {projectBoard.name}</h4>
<TrackerModal <TrackerModal
active={modalAddWorker} active={modalAddWorker}
setActive={setModalAddWorker} setActive={setModalAddWorker}
></TrackerModal> ></TrackerModal>
<div className="tasks__head__persons"> <div className="tasks__head__persons">
{/*<img src={avatarTest} alt="avatar" />*/} {/*<img src={avatarTest} alt="avatar" />*/}
{/*<img src={avatarTest} alt="avatar" />*/} {/*<img src={avatarTest} alt="avatar" />*/}
<span className="countPersons">{projectBoard.projectUsers?.length}</span> <span className="countPersons">
<span {projectBoard.projectUsers?.length}
className="addPerson" </span>
onClick={() => { <span
setPersonListOpen(true) className="addPerson"
}} onClick={() => {
> setPersonListOpen(true);
+ }}
</span> >
<p>добавить участника</p> +
{personListOpen && </span>
<div className='persons__list'> <p>добавить участника</p>
<img className='persons__list__close' src={close} alt='close' onClick={() => setPersonListOpen(false)} /> {personListOpen && (
<div className='persons__list__count'><span>{projectBoard.projectUsers?.length}</span>участник</div> <div className="persons__list">
<div className='persons__list__info'>В проекте - <span>{projectBoard.name}</span></div> <img
<div className='persons__list__items'> className="persons__list__close"
{projectBoard.projectUsers?.map((person) => { src={close}
return <div className='persons__list__item' key={person.user_id}> alt="close"
<img className='avatar' src={urlForLocal(person.user.avatar)} alt='avatar' /> onClick={() => setPersonListOpen(false)}
<span>{person.user.fio}</span> />
<img className='delete' src={close} alt='delete' onClick={() => deletePerson(person.user_id)}/> <div className="persons__list__count">
<span>{projectBoard.projectUsers?.length}</span>
участник
</div>
<div className="persons__list__info">
В проекте - <span>{projectBoard.name}</span>
</div>
<div className="persons__list__items">
{projectBoard.projectUsers?.map((person) => {
return (
<div
className="persons__list__item"
key={person.user_id}
>
<img
className="avatar"
src={urlForLocal(person.user.avatar)}
alt="avatar"
/>
<span>{person.user.fio}</span>
<img
className="delete"
src={close}
alt="delete"
onClick={() => deletePerson(person.user_id)}
/>
</div>
);
})}
</div>
<div
className="persons__list__add"
onClick={() => {
dispatch(modalToggle("addWorker"));
setModalAddWorker(true);
setPersonListOpen(false);
}}
>
<span className="addPerson">+</span>
<p>Добавить участников</p>
</div>
</div> </div>
}) )}
}
</div> </div>
<div className='persons__list__add' <div className="tasks__head__select">
onClick={() => { <span>Учавствую</span>
dispatch(modalToggle("addWorker")); <img src={selectArrow} alt="arrow" />
setModalAddWorker(true);
setPersonListOpen(false)
}}
>
<span className='addPerson'>+</span>
<p>Добавить участников</p>
</div> </div>
</div> <div className="tasks__head__select">
} <span>Мои</span>
</div> <img src={selectArrow} alt="arrow" />
<div className="tasks__head__select"> </div>
<span>Учавствую</span> <Link to={`/profile/tracker`} className="link">
<img src={selectArrow} alt="arrow" /> <div className="tasks__head__back">
</div> <p>Вернуться на проекты</p>
<div className="tasks__head__select"> <img src={arrow} alt="arrow" />
<span>Мои</span>
<img src={selectArrow} alt="arrow" />
</div>
<Link to={`/profile/tracker`} className="link">
<div className="tasks__head__back">
<p>Вернуться на проекты</p>
<img src={arrow} alt="arrow" />
</div>
</Link>
</div>
</div>
</div>
<div className="modal-tiket__content ticket">
<div className="content ticket-whith">
<div className="content__task">
<span>Задача</span>
{editOpen ? <input value={inputsValue.title} onChange={(e) => {
setInputsValue((prevValue) => ({...prevValue, title: e.target.value}))
}} /> :<h5>{inputsValue.title}</h5>}
<div className="content__description">
{editOpen ? <input value={inputsValue.description} onChange={(e) => {
setInputsValue((prevValue) => ({...prevValue, description: e.target.value}))
}}/> :<p>{inputsValue.description}</p>}
{/*<img src={task} className="image-task"></img>*/}
</div>
<div className="content__communication">
<p className="tasks">
<button
onClick={() => {
dispatch(modalToggle("addSubtask"));
setModalAddWorker(true);
}}
>
<img src={plus}></img>
Добавить под задачу
</button>
</p>
<p className="file">
<button>
<img src={file}></img>
Загрузить файл
</button>
<span>{0}</span>
Файлов
</p>
</div>
<div className="content__input">
<input placeholder="Оставить комментарий" value={inputsValue.comment} onChange={(e) => {
setInputsValue((prevValue) => ({...prevValue, comment: e.target.value}))
}} />
<img src={send} onClick={createComment}></img>
</div>
<div className='comments__list'>
{comments.map((comment) => {
return <div className='comments__list__item' key={comment.id}>
<div className='comments__list__item__info'>
<span>{getCorrectDate(comment.created_at)}</span>
<div className={commentsEditOpen[comment.id] ? 'edit edit__open' : 'edit'} >
<img src={edit} alt='edit' onClick={() => {
if (commentsEditOpen[comment.id]) {
editComment(comment.id)
}
setCommentsEditOpen((prevValue) => ({...prevValue, [comment.id]: !prevValue[comment.id]}))
}} />
</div>
<img src={del} alt='delete' onClick={() => deleteComment(comment.id)} />
</div> </div>
{commentsEditOpen[comment.id] ? <input value={commentsEditText[comment.id]} onChange={(e) => { </Link>
setCommentsEditText((prevValue) => ({...prevValue, [comment.id]: e.target.value})) </div>
}} /> : <p>{commentsEditText[comment.id]}</p>} </div>
</div>
<div className="modal-tiket__content ticket">
<div className="content ticket-whith">
<div className="content__task">
<span>Задача</span>
{editOpen ? (
<input
value={inputsValue.title}
onChange={(e) => {
setInputsValue((prevValue) => ({
...prevValue,
title: e.target.value,
}));
}}
/>
) : (
<h5>{inputsValue.title}</h5>
)}
<div className="content__description">
{editOpen ? (
<input
value={inputsValue.description}
onChange={(e) => {
setInputsValue((prevValue) => ({
...prevValue,
description: e.target.value,
}));
}}
/>
) : (
<p>{inputsValue.description}</p>
)}
{/*<img src={task} className="image-task"></img>*/}
</div> </div>
}) <div className="content__communication">
<p className="tasks">
} <button
</div> onClick={() => {
</div> dispatch(modalToggle("addSubtask"));
</div> setModalAddWorker(true);
<div className="workers"> }}
<div className="workers_box"> >
<p className="workers__creator"> <img src={plus}></img>
Создатель : <span>{taskInfo.user?.fio}</span> Добавить под задачу
</p> </button>
<div> </p>
{Boolean(taskInfo.taskUsers?.length) && <p className="file">
taskInfo.taskUsers.map((worker, index) => { <button>
return ( <img src={file}></img>
<div className="worker" key={index}> Загрузить файл
<img src={worker.avatar}></img> </button>
<p>{worker.name}</p> <span>{0}</span>
</div> Файлов
); </p>
})} </div>
<div className="content__input">
<input
placeholder="Оставить комментарий"
value={inputsValue.comment}
onChange={(e) => {
setInputsValue((prevValue) => ({
...prevValue,
comment: e.target.value,
}));
}}
/>
<img src={send} onClick={createComment}></img>
</div>
<div className="comments__list">
{comments.map((comment) => {
return (
<div className="comments__list__item" key={comment.id}>
<div className="comments__list__item__info">
<span>{getCorrectDate(comment.created_at)}</span>
<div
className={
commentsEditOpen[comment.id]
? "edit edit__open"
: "edit"
}
>
<img
src={edit}
alt="edit"
onClick={() => {
if (commentsEditOpen[comment.id]) {
editComment(comment.id);
}
setCommentsEditOpen((prevValue) => ({
...prevValue,
[comment.id]: !prevValue[comment.id],
}));
}}
/>
</div>
<img
src={del}
alt="delete"
onClick={() => deleteComment(comment.id)}
/>
</div>
{commentsEditOpen[comment.id] ? (
<input
value={commentsEditText[comment.id]}
onChange={(e) => {
setCommentsEditText((prevValue) => ({
...prevValue,
[comment.id]: e.target.value,
}));
}}
/>
) : (
<p>{commentsEditText[comment.id]}</p>
)}
</div>
);
})}
</div>
</div>
</div> </div>
<div className="workers">
<div className="workers_box">
<p className="workers__creator">
Создатель : <span>{taskInfo.user?.fio}</span>
</p>
<div>
{Boolean(taskInfo.taskUsers?.length) &&
taskInfo.taskUsers.map((worker, index) => {
return (
<div className="worker" key={index}>
<img src={worker.avatar}></img>
<p>{worker.name}</p>
</div>
);
})}
</div>
<div className="add-worker moreItems"> <div className="add-worker moreItems">
<button <button
onClick={() => {
dispatch(modalToggle("addWorker"));
setModalAddWorker(true);
}}
>
+
</button>
<span>Добавить исполнителя</span>
</div>
<div className="add-worker moreItems">
<button
onClick={() => {
dispatch(modalToggle("addWorker"));
setModalAddWorker(true);
}}
>
+
</button>
<span>Добавить участников</span>
</div>
</div>
<div className="workers_box-middle">
<div className="time">
<img src={watch}></img>
<span>Длительность : </span>
<p>{"0:00:00"}</p>
</div>
<button className="start">
Начать делать <img src={arrow2}></img>
</button>
</div>
<div className="workers_box-bottom">
<div
className={editOpen ? "edit" : ""}
onClick={() => { onClick={() => {
dispatch(modalToggle("addWorker")); if (editOpen) {
setModalAddWorker(true); setEditOpen(!editOpen);
editTask();
} else {
setEditOpen(!editOpen);
}
}} }}
> >
+ <img src={edit}></img>
</button> <p>{editOpen ? "сохранить" : "редактировать"}</p>
<span>Добавить исполнителя</span> </div>
</div> <div>
<div className="add-worker moreItems"> <img src={link}></img>
<button <p>ссылка на проект</p>
onClick={() => { </div>
dispatch(modalToggle("addWorker")); <div>
setModalAddWorker(true); <img src={archive2}></img>
}} <p>в архив</p>
> </div>
+ <div onClick={deleteTask}>
</button> <img src={del}></img>
<span>Добавить участников</span> <p>удалить</p>
</div>
</div>
</div> </div>
</div> </div>
</>
<div className="workers_box-middle"> )}
<div className="time">
<img src={watch}></img>
<span>Длительность : </span>
<p>{"0:00:00"}</p>
</div>
<button className="start">
Начать делать <img src={arrow2}></img>
</button>
</div>
<div className="workers_box-bottom">
<div className={editOpen ? 'edit' : ''} onClick={() => {
if(editOpen) {
setEditOpen(!editOpen)
editTask()
} else {
setEditOpen(!editOpen)
}
}}>
<img src={edit}></img>
<p>{editOpen ? 'сохранить' : 'редактировать'}</p>
</div>
<div>
<img src={link}></img>
<p>ссылка на проект</p>
</div>
<div>
<img src={archive2}></img>
<p>в архив</p>
</div>
<div onClick={deleteTask}>
<img src={del}></img>
<p>удалить</p>
</div>
</div>
</div>
</div>
</>
}
</div> </div>
<Footer /> <Footer />
</section> </section>

View File

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

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 B

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

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

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 268 B

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

8
src/images/remove.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 475 B

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

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

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

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

View File

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

View File

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

View File

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

View File

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