Merge pull request 'trackerTask' (#12) from trackerTask into main

Reviewed-on: #12
This commit is contained in:
Николай Полтщук 2024-02-01 23:15:28 +03:00
commit 06f5cda1e8
14 changed files with 381 additions and 197 deletions

View File

@ -101,6 +101,21 @@
line-height: 32px; line-height: 32px;
color: white; color: white;
height: 45px; height: 45px;
@media (max-width: 900px) {
max-width: 185px;
}
@media (max-width: 450px) {
width: 100%;
max-width: none;
margin-right: 0;
}
}
@media (max-width: 450px) {
flex-direction: column;
row-gap: 10px;
} }
} }
@ -139,6 +154,11 @@
transform: scale(1.03); transform: scale(1.03);
color: white; color: white;
} }
@media (max-width: 450px) {
width: 100%;
max-width: none;
}
} }
&__img { &__img {

View File

@ -1,4 +1,5 @@
import React from "react"; import React from "react";
import { Link } from "react-router-dom";
import close from "assets/icons/closeProjectPersons.svg"; import close from "assets/icons/closeProjectPersons.svg";
@ -16,12 +17,13 @@ export const ModalTrackerRegistration = ({ setModalReset, email }) => {
для активации вашего аккаунта на почту для активации вашего аккаунта на почту
<br /> <span>{email}</span> <br /> <span>{email}</span>
</p> </p>
<button <Link
to={"/registration-setting"}
onClick={() => setModalReset(false)} onClick={() => setModalReset(false)}
className="modalConfirmTracker__btn" className="modalConfirmTracker__btn"
> >
Понятно Понятно
</button> </Link>
<img <img
onClick={() => setModalReset(false)} onClick={() => setModalReset(false)}
src={close} src={close}

View File

@ -0,0 +1,29 @@
import React from "react";
import { Link } from "react-router-dom";
import { urlForLocal } from "@utils/helper";
import rightArrow from "assets/icons/arrows/arrowRight.svg";
import "./partnerPersonCard.scss";
export const PartnerPersonCard = ({ name, img, userId }) => {
return (
<div className="partnerPersonCard">
<div className="partnerPersonCard__img">
<img src={urlForLocal(img)} alt="avatar" />
</div>
<div className="partnerPersonCard__info">
<h2 className="partnerPersonCard__name">{name}</h2>
<Link className="partnerPersonCard__report" to={`calendar/${userId}`}>
Подробный отчет
<div className="partnerPersonCard__more">
<img src={rightArrow} alt="arrow" />
</div>
</Link>
</div>
</div>
);
};
export default PartnerPersonCard;

View File

@ -0,0 +1,45 @@
.partnerPersonCard {
display: flex;
position: relative;
&__img {
background: white;
border-radius: 12px;
padding: 25px;
z-index: 2;
img {
width: 77px;
height: 77px;
}
}
&__info {
position: relative;
background: #E1FCCF;
z-index: 1;
padding: 15px 15px 15px 30px;
left: -15px;
border-radius: 12px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
&__report {
display: flex;
column-gap: 10px;
color: black;
font-size: 16px;
align-items: center;
}
&__more {
width: 41px;
height: 41px;
background: white;
border-radius: 50px;
display: flex;
justify-content: center;
align-items: center;
}
}

View File

@ -51,9 +51,7 @@ export const ProfileCalendar = () => {
return; return;
} }
apiRequest( apiRequest(
`/reports/reports-by-date?${requestDates}&user_card_id=${localStorage.getItem( `/reports/index?${requestDates}&user_id =${localStorage.getItem("id")}`
"cardId"
)}`
).then((reports) => { ).then((reports) => {
let spendTime = 0; let spendTime = 0;

View File

@ -114,9 +114,7 @@ export const ProfileCalendarComponent = React.memo(
startDate._d startDate._d
)}`; )}`;
apiRequest( apiRequest(
`/reports/reports-by-date?${requestDates}&user_card_id=${localStorage.getItem( `/reports/index?${requestDates}&user_id =${localStorage.getItem("id")}`
"cardId"
)}`
).then((reports) => { ).then((reports) => {
let spendTime = 0; let spendTime = 0;
reports.map((report) => { reports.map((report) => {
@ -273,15 +271,15 @@ export const ProfileCalendarComponent = React.memo(
? `${getCorrectDate(startDate)} - ${getCorrectDate(endDate)}` ? `${getCorrectDate(startDate)} - ${getCorrectDate(endDate)}`
: `${getCorrectDate(endDate)} - ${getCorrectDate(startDate)}` : `${getCorrectDate(endDate)} - ${getCorrectDate(startDate)}`
: activePeriod : activePeriod
? "Выберите диапазон на календаре" ? "Выберите диапазон на календаре"
: "Выбрать диапазон"} : "Выбрать диапазон"}
</span> </span>
<span> <span>
{totalRangeHours {totalRangeHours
? `${totalRangeHours} ${hourOfNum(totalRangeHours)}` ? `${totalRangeHours} ${hourOfNum(totalRangeHours)}`
: endDate : endDate
? "0 часов" ? "0 часов"
: ""} : ""}
</span> </span>
{endDate && ( {endDate && (
<BaseButton <BaseButton

View File

@ -97,6 +97,7 @@ const ReportForm = () => {
apiRequest("/reports/create", { apiRequest("/reports/create", {
method: "POST", method: "POST",
data: { data: {
user_id: localStorage.getItem("id"),
tasks: inputs, tasks: inputs,
difficulties: troublesInputValue, difficulties: troublesInputValue,
tomorrow: scheduledInputValue, tomorrow: scheduledInputValue,

View File

@ -39,9 +39,7 @@ export const ShortReport = () => {
setTomorrowTask([]); setTomorrowTask([]);
setTotalHours(0); setTotalHours(0);
apiRequest( apiRequest(
`reports/find-by-date?user_card_id=${localStorage.getItem( `reports/find-by-date?user_id=${localStorage.getItem("id")}&date=${day}`
"cardId"
)}&date=${day}`
).then((res) => { ).then((res) => {
let spendTime = 0; let spendTime = 0;
for (const item of res) { for (const item of res) {

View File

@ -504,13 +504,13 @@ $maxWidthContainer: 1123;
} }
} }
&__finished { &__finished {
background: rgba(255, 255, 255, 0.76); background: white;
mix-blend-mode: normal; mix-blend-mode: normal;
border: 3px solid #52b709; border: 3px solid #52b709;
border-radius: 12px; border-radius: 12px;
padding: 13px 16px; padding: 13px 16px;
position: absolute; position: absolute;
bottom: 0; bottom: 4px;
left: 0; left: 0;
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -1,137 +1,146 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { useDispatch } from "react-redux"; // import { useDispatch } from "react-redux";
import { Link } from "react-router-dom"; // import { Link } from "react-router-dom";
import { Navigate } from "react-router-dom"; import { Navigate } from "react-router-dom";
import { setPartnerEmployees } from "@redux/outstaffingSlice"; import { apiRequest } from "@api/request";
// import { setPartnerEmployees } from "@redux/outstaffingSlice";
import { Footer } from "@components/Common/Footer/Footer"; import { Footer } from "@components/Common/Footer/Footer";
import { Navigation } from "@components/Navigation/Navigation";
import PartnerPersonCard from "@components/PartnerPersonCard/PartnerPersonCard";
import { ProfileBreadcrumbs } from "@components/ProfileBreadcrumbs/ProfileBreadcrumbs"; import { ProfileBreadcrumbs } from "@components/ProfileBreadcrumbs/ProfileBreadcrumbs";
import { ProfileHeader } from "@components/ProfileHeader/ProfileHeader"; import { ProfileHeader } from "@components/ProfileHeader/ProfileHeader";
import rightArrow from "assets/icons/arrows/arrowRight.svg"; // import rightArrow from "assets/icons/arrows/arrowRight.svg";
import avatarImg from "assets/images/avatarMok.png"; // import avatarImg from "assets/images/avatarMok.png";
import AdminImg from "assets/images/partnerProfile/PersonalAdmin.svg"; // import AdminImg from "assets/images/partnerProfile/PersonalAdmin.svg";
import ArchitectureImg from "assets/images/partnerProfile/PersonalArchitecture.svg"; // import ArchitectureImg from "assets/images/partnerProfile/PersonalArchitecture.svg";
import CopyImg from "assets/images/partnerProfile/PersonalCopy.svg"; // import CopyImg from "assets/images/partnerProfile/PersonalCopy.svg";
import DesignImg from "assets/images/partnerProfile/PersonalDesign.svg"; // import DesignImg from "assets/images/partnerProfile/PersonalDesign.svg";
import FrontendImg from "assets/images/partnerProfile/PersonalFrontend.svg"; // import FrontendImg from "assets/images/partnerProfile/PersonalFrontend.svg";
import ManageImg from "assets/images/partnerProfile/PersonalMng.svg"; // import ManageImg from "assets/images/partnerProfile/PersonalMng.svg";
import SmmImg from "assets/images/partnerProfile/PersonalSMM.svg"; // import SmmImg from "assets/images/partnerProfile/PersonalSMM.svg";
import TestImg from "assets/images/partnerProfile/PersonalTesters.svg"; // import TestImg from "assets/images/partnerProfile/PersonalTesters.svg";
import BackEndImg from "assets/images/partnerProfile/personalBackEnd.svg"; // import BackEndImg from "assets/images/partnerProfile/personalBackEnd.svg";
import { Navigation } from "../../components/Navigation/Navigation";
import "./partnerСategories.scss"; import "./partnerСategories.scss";
export const PartnerCategories = () => { export const PartnerCategories = () => {
const dispatch = useDispatch(); // const dispatch = useDispatch();
if (localStorage.getItem("role_status") !== "18") { if (localStorage.getItem("role_status") !== "18") {
return <Navigate to="/profile" replace />; return <Navigate to="/profile" replace />;
} }
const [personalInfoItems] = useState([ const [staff, setStaff] = useState([]);
{
title: "Backend разработчики",
link: "/profile/categories/employees",
description:
"Java PHP Python C# React Vue.js NodeJs Golang Ruby JavaScript",
available: true,
img: BackEndImg
},
{
title: "Frontend разработчики",
link: "/profile/categories/employees",
description:
"Java PHP Python C# React Vue.js NodeJs Golang Ruby JavaScript",
available: true,
img: FrontendImg
},
{
title: "Архитектура проектов",
link: "/profile/categories/employees",
description: "Потоки данных ER ERP CRM CQRS UML BPMN",
available: true,
img: ArchitectureImg
},
{
title: "Дизайн проектов",
link: "/profile/categories/employees",
description:
"Java PHP Python C# React Vue.js NodeJs Golang Ruby JavaScript",
available: true,
img: DesignImg
},
{
title: "Тестирование проектов",
link: "/profile/add-request",
description: "SQL Postman TestRail Kibana Ручное тестирование",
available: false,
img: TestImg
},
{
title: "Администрирование проектов",
link: "/profile/add-request",
description: "DevOps ELK Kubernetes Docker Bash Apache Oracle Git",
available: false,
img: AdminImg
},
{
title: "Управление проектом",
link: "/profile/add-request",
description: "Scrum Kanban Agile Miro CustDev",
available: false,
img: ManageImg
},
{
title: "Копирайтинг проектов",
link: "/profile/add-request",
description: "Теги Заголовок H1 Дескриптор Абзац Сценарий",
available: false,
img: CopyImg
},
{
title: "Реклама и SMM",
link: "/profile/add-request",
description:
"Java PHP Python C# React Vue.js NodeJs Golang Ruby JavaScript",
available: false,
img: SmmImg
}
]);
const [mokPersons] = useState([ useEffect(() => {
{ apiRequest("/project/my-employee").then((el) => {
personAvatar: avatarImg, setStaff(el.managerEmployees);
name: "Макаренко Дмитрий", });
qualification: "PHP Backend - разработчик", }, []);
level: "Middle",
project: "Админка НВД Консалтинг", // const [personalInfoItems] = useState([
tasks_in_progress: 5, // {
month_hours: 140, // title: "Backend разработчики",
id: 1 // link: "/profile/categories/employees",
}, // description:
{ // "Java PHP Python C# React Vue.js NodeJs Golang Ruby JavaScript",
personAvatar: avatarImg, // available: true,
name: "Макаренко Дмитрий", // img: BackEndImg
qualification: "PHP Backend - разработчик", // },
level: "Middle", // {
project: "Админка НВД Консалтинг", // title: "Frontend разработчики",
tasks_in_progress: 5, // link: "/profile/categories/employees",
month_hours: 140, // description:
id: 2 // "Java PHP Python C# React Vue.js NodeJs Golang Ruby JavaScript",
}, // available: true,
{ // img: FrontendImg
personAvatar: avatarImg, // },
name: "Макаренко Дмитрий", // {
qualification: "PHP Backend - разработчик", // title: "Архитектура проектов",
level: "Middle", // link: "/profile/categories/employees",
project: "Админка НВД Консалтинг", // description: "Потоки данных ER ERP CRM CQRS UML BPMN",
tasks_in_progress: 5, // available: true,
month_hours: 140, // img: ArchitectureImg
id: 3 // },
} // {
]); // title: "Дизайн проектов",
// link: "/profile/categories/employees",
// description:
// "Java PHP Python C# React Vue.js NodeJs Golang Ruby JavaScript",
// available: true,
// img: DesignImg
// },
// {
// title: "Тестирование проектов",
// link: "/profile/add-request",
// description: "SQL Postman TestRail Kibana Ручное тестирование",
// available: false,
// img: TestImg
// },
// {
// title: "Администрирование проектов",
// link: "/profile/add-request",
// description: "DevOps ELK Kubernetes Docker Bash Apache Oracle Git",
// available: false,
// img: AdminImg
// },
// {
// title: "Управление проектом",
// link: "/profile/add-request",
// description: "Scrum Kanban Agile Miro CustDev",
// available: false,
// img: ManageImg
// },
// {
// title: "Копирайтинг проектов",
// link: "/profile/add-request",
// description: "Теги Заголовок H1 Дескриптор Абзац Сценарий",
// available: false,
// img: CopyImg
// },
// {
// title: "Реклама и SMM",
// link: "/profile/add-request",
// description:
// "Java PHP Python C# React Vue.js NodeJs Golang Ruby JavaScript",
// available: false,
// img: SmmImg
// }
// ]);
// const [mokPersons] = useState([
// {
// personAvatar: avatarImg,
// name: "Макаренко Дмитрий",
// qualification: "PHP Backend - разработчик",
// level: "Middle",
// project: "Админка НВД Консалтинг",
// tasks_in_progress: 5,
// month_hours: 140,
// id: 1
// },
// {
// personAvatar: avatarImg,
// name: "Макаренко Дмитрий",
// qualification: "PHP Backend - разработчик",
// level: "Middle",
// project: "Админка НВД Консалтинг",
// tasks_in_progress: 5,
// month_hours: 140,
// id: 2
// },
// {
// personAvatar: avatarImg,
// name: "Макаренко Дмитрий",
// qualification: "PHP Backend - разработчик",
// level: "Middle",
// project: "Админка НВД Консалтинг",
// tasks_in_progress: 5,
// month_hours: 140,
// id: 3
// }
// ]);
return ( return (
<div className="partnerCategories"> <div className="partnerCategories">
<ProfileHeader /> <ProfileHeader />
@ -145,39 +154,49 @@ export const PartnerCategories = () => {
/> />
<h2 className="partnerCategories__title">Данные персонала</h2> <h2 className="partnerCategories__title">Данные персонала</h2>
<div className="partnerCategories__items"> <div className="partnerCategories__items">
{personalInfoItems.map((item, index) => { {staff.map((card) => {
return ( return (
<Link <PartnerPersonCard
to={item.link} key={card.id}
key={index} name={card.employee.fio}
className={ img={card.employee.avatar}
item.available userId={card.user_id}
? "partnerCategories__item item" />
: "partnerCategories__item item item__disable"
}
onClick={() => {
dispatch(setPartnerEmployees(mokPersons));
}}
>
<div className="item__title">
<img src={item.img} alt={item.title} />
<h4>{item.title}</h4>
</div>
<div className="item__info">
<p>{item.description}</p>
<div className="more">
<img src={rightArrow} alt="arrow" />
</div>
</div>
{!item.available && (
<div className="item__disableHover">
<p>У вас нет персонала из категории</p>
<button>Подобрать</button>
</div>
)}
</Link>
); );
})} })}
{/*{personalInfoItems.map((item, index) => {*/}
{/* return (*/}
{/* <Link*/}
{/* to={item.link}*/}
{/* key={index}*/}
{/* className={*/}
{/* item.available*/}
{/* ? "partnerCategories__item item"*/}
{/* : "partnerCategories__item item item__disable"*/}
{/* }*/}
{/* onClick={() => {*/}
{/* dispatch(setPartnerEmployees(mokPersons));*/}
{/* }}*/}
{/* >*/}
{/* <div className="item__title">*/}
{/* <img src={item.img} alt={item.title} />*/}
{/* <h4>{item.title}</h4>*/}
{/* </div>*/}
{/* <div className="item__info">*/}
{/* <p>{item.description}</p>*/}
{/* <div className="more">*/}
{/* <img src={rightArrow} alt="arrow" />*/}
{/* </div>*/}
{/* </div>*/}
{/* {!item.available && (*/}
{/* <div className="item__disableHover">*/}
{/* <p>У вас нет персонала из категории</p>*/}
{/* <button>Подобрать</button>*/}
{/* </div>*/}
{/* )}*/}
{/* </Link>*/}
{/* );*/}
{/*})}*/}
</div> </div>
</div> </div>
<Footer /> <Footer />

View File

@ -13,10 +13,12 @@
&__items { &__items {
display: flex; display: flex;
gap: 10px;
flex-wrap: wrap; flex-wrap: wrap;
margin-top: 25px; //flex-wrap: wrap;
row-gap: 24px; //margin-top: 25px;
column-gap: 21px; //row-gap: 24px;
//column-gap: 21px;
} }

View File

@ -1,4 +1,8 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Navigate } from "react-router-dom";
import { useFormValidation } from "@hooks/useFormValidation";
import { useNotification } from "@hooks/useNotification";
import AuthHeader from "@components/Common/AuthHeader/AuthHeader"; import AuthHeader from "@components/Common/AuthHeader/AuthHeader";
import { Footer } from "@components/Common/Footer/Footer"; import { Footer } from "@components/Common/Footer/Footer";
@ -14,9 +18,38 @@ import "./trackerRegistration.scss";
export const TrackerRegistration = () => { export const TrackerRegistration = () => {
const [modalConfirmOpen, setModalConfirm] = useState(false); const [modalConfirmOpen, setModalConfirm] = useState(false);
const [inputs, setInputs] = useState({ const fields = {
email: "" username: "",
}); email: "",
password: "",
secondPassword: ""
};
const apiEndpoint = "/register/sign-up";
const { showNotification } = useNotification();
const showNotificationError = () => {
showNotification({
show: true,
text: "Аккаунт с таким логином или email уже существует",
type: "error"
});
};
const showNotificationTrue = () => {
showNotification({
show: true,
text: "Аккаунт успешно создан",
type: "success"
});
};
const { formData, validationErrors, handleChange, handleSubmit } =
useFormValidation(
apiEndpoint,
fields,
showNotificationError,
showNotificationTrue
);
return ( return (
<div className="tracker-registration"> <div className="tracker-registration">
@ -34,33 +67,65 @@ export const TrackerRegistration = () => {
<div className="tracker-registration__form"> <div className="tracker-registration__form">
<div className="tracker-registration__form__inputs"> <div className="tracker-registration__form__inputs">
<div className="tracker-registration__inputContainer"> <div className="tracker-registration__inputContainer">
<span>Ваше имя</span> <h5>Ваше имя</h5>
<input placeholder="Имя" />
</div>
<div className="tracker-registration__inputContainer">
<span>Ваш e-mail</span>
<input <input
placeholder="E-mail" placeholder="Имя"
onChange={(e) => className={validationErrors.username ? "error" : ""}
setInputs((prevState) => ({ onChange={handleChange}
...prevState, value={formData.username}
email: e.target.value id="username"
}))
}
type="email"
/> />
<span>{validationErrors.username}</span>
</div> </div>
<div className="tracker-registration__inputContainer"> <div className="tracker-registration__inputContainer">
<span>Придумайте пароль</span> <h5>Ваш e-mail</h5>
<input placeholder="Пароль" /> <input
onChange={handleChange}
className={validationErrors.email ? "error" : ""}
placeholder="E-mail"
type="email"
id="email"
value={formData.email}
/>
<span>{validationErrors.email}</span>
</div> </div>
<div className="tracker-registration__inputContainer"> <div className="tracker-registration__inputContainer">
<span>Повторите пароль</span> <h5>Придумайте пароль</h5>
<input placeholder="Повторите пароль" /> <input
placeholder="Пароль"
className={validationErrors.password ? "error" : ""}
onChange={handleChange}
value={formData.password}
type="password"
id="password"
/>
<span>{validationErrors.password}</span>
</div>
<div className="tracker-registration__inputContainer">
<h5>Повторите пароль</h5>
<input
placeholder="Повторите пароль"
className={validationErrors.secondPassword ? "error" : ""}
value={formData.secondPassword}
type="password"
onChange={handleChange}
id="secondPassword"
/>
<span>{validationErrors.secondPassword}</span>
</div> </div>
</div> </div>
<div className="tracker-registration__form__submit"> <div className="tracker-registration__form__submit">
<button onClick={() => setModalConfirm(true)}>Отправить</button> <button
onClick={async (e) => {
e.preventDefault();
const result = await handleSubmit(e);
if (result === true) {
setModalConfirm(true);
}
}}
>
Отправить
</button>
<div className="tracker-registration__form__info"> <div className="tracker-registration__form__info">
<img src={authImg} alt="img" /> <img src={authImg} alt="img" />
<p> <p>
@ -80,7 +145,7 @@ export const TrackerRegistration = () => {
<ModalLayout active={modalConfirmOpen} setActive={setModalConfirm}> <ModalLayout active={modalConfirmOpen} setActive={setModalConfirm}>
<ModalTrackerRegistration <ModalTrackerRegistration
setModalReset={setModalConfirm} setModalReset={setModalConfirm}
email={inputs.email} email={formData.email}
/> />
</ModalLayout> </ModalLayout>
)} )}

View File

@ -41,6 +41,7 @@
&__submit { &__submit {
display: flex; display: flex;
align-items: center; align-items: center;
margin-top: 5px;
button { button {
border-radius: 44px; border-radius: 44px;
@ -106,15 +107,18 @@
&__inputContainer { &__inputContainer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
row-gap: 15px;
max-width: 300px; max-width: 300px;
width: 100%; width: 100%;
margin-bottom: 44px; margin-bottom: 44px;
h5 {
font-size: 15px;
}
span { span {
font-weight: 400; font-weight: 400;
font-size: 15px; font-size: 12px;
color: #000; color: red;
} }
input { input {
@ -122,6 +126,7 @@
padding: 8px 12px 9px; padding: 8px 12px 9px;
background-color: #EFF2F7; background-color: #EFF2F7;
border-radius: 8px; border-radius: 8px;
margin: 10px 0;
border: none; border: none;
font-weight: 400; font-weight: 400;
font-size: 15px; font-size: 15px;
@ -132,5 +137,9 @@
margin-bottom: 0; margin-bottom: 0;
max-width: none; max-width: none;
} }
.error {
border: 1px solid red;
}
} }
} }

View File

@ -41,9 +41,7 @@ export const ViewReport = () => {
setDifficulties([]); setDifficulties([]);
setTomorrowTask([]); setTomorrowTask([]);
apiRequest( apiRequest(
`reports/find-by-date?user_card_id=${localStorage.getItem( `reports/find-by-date?user_id=${localStorage.getItem("id")}&date=${day}`
"cardId"
)}&date=${day}`
).then((res) => { ).then((res) => {
let spendTime = 0; let spendTime = 0;
for (const item of res) { for (const item of res) {