Merge pull request #56 from apuc/tracker

Tracker
This commit is contained in:
NikoM1k 2023-03-13 22:34:42 +02:00 committed by GitHub
commit e505646a39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1111 additions and 236 deletions

View File

@ -70,7 +70,7 @@ export const ProfileCalendarComponent = React.memo(({value, setValueHandler, rep
<div className='calendar-component__header-info'>
<h3>Мои отчеты:</h3>
<p className='calendar__hours'>
{month} - <span> {totalHours} {hourOfNum(totalHours)} </span>
{month}&nbsp;<span>{totalHours} {hourOfNum(totalHours)} </span>
</p>
</div>
<div className='calendar-component__header-switcher'>

View File

@ -18,7 +18,6 @@ import {getReportDate} from '../../redux/reportSlice'
import calendarIcon from '../../images/calendar_icon.png'
import ellipse from '../../images/ellipse.png'
import remove from '../../images/remove.png'
import addIcon from '../../images/addIcon.png'
import arrow from "../../images/right-arrow.png";
import './reportForm.scss'
@ -191,7 +190,7 @@ const ReportForm = () => {
})}
<div className='report-form__form-add'>
<img onClick={addInput} src={addIcon} alt=''/>
<p className='addMore' onClick={addInput}>+</p>
<span>Добавить еще </span>
</div>
</div>

View File

@ -189,6 +189,8 @@
img {
cursor: pointer;
width: 23px;
height: 23px;
}
}
@ -273,6 +275,21 @@
&__form-add {
margin-left: 28px;
display: flex;
align-items: center;
.addMore {
display: flex;
align-items: center;
justify-content: center;
color: #4CAF50;
width: 38px;
height: 38px;
background: #E8E8E8;
margin-bottom: 0;
border-radius: 50px;
font-size: 32px;
}
span {
font-family: 'GT Eesti Pro Display';

View File

@ -0,0 +1,27 @@
import React from "react";
import "./ModalProject.scss";
export const ModalProject = ({ active, setActive }) => {
return (
<div
className={active ? "modal-project active" : "modal-project"}
onClick={() => setActive(false)}
>
<div
className="modal-project__content"
onClick={(e) => e.stopPropagation()}
>
<div className="title-project">
<h4>Укажите название проекта: </h4>
<div className="input-container">
<input className="name-project"></input>
</div>
</div>
<button className="create-project">Создать</button>
</div>
</div>
);
};
export default ModalProject;

View File

@ -0,0 +1,57 @@
.modal-project {
z-index: 9;
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, 0.11);
position: fixed;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
transform: scale(0);
}
.modal-project.active {
transform: scale(1);
}
.modal-project__content {
padding: 15px;
background: #ffffff;
border: 1px solid #dde2e4;
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
.title-project {
display: flex;
align-items: center;
flex-direction: column;
.input-container {
width: 220px;
height: 25px;
border-radius: 44px;
border: 1px solid #d1d1d1;
}
h4 {
margin-bottom: 10px;
}
}
.name-project {
margin-left: 10px;
border: none;
outline: none;
height: 100%;
width: 90%;
font-size: 14px;
}
.create-project {
margin: 15px 0 0 0;
}
}

View File

@ -0,0 +1,111 @@
import React, { useState } from "react";
import "./ModalTiket.scss";
import creatorMock from "../../../images/avatarMoсkCreator.png";
import avatarMock1 from "../../../images/avatarMoсk1.png";
import avatarMock2 from "../../../images/avatarMoсk2.png";
import category from "../../../images/category.png";
import comments from "../../../images/commentsBoard.svg";
import watch from "../../../images/watch.png";
import files from "../../../images/filesBoard.svg";
import task from "../../../images/tasksMock.png";
import arrow from "../../../images/arrowStart.png";
export const ModalTiket = ({ active, setActive }) => {
const [tiket] = useState({
name: "Разработка трекера",
code: "PR - 2245",
creator: "Василий Тарасов",
descriptions:
"На многих страницах сайта отсутствуют или некорректно заполнены метатеги Description. Это может негативно повлиять на представление сайта в результатах поиска.Необходимо исправить все страницы где есть ошибки или отсутствует Title и Description.",
});
const [workers] = useState([
{
name: "Дмитрий Рогов",
avatar: avatarMock2,
},
{
name: "Марина Серова",
avatar: avatarMock1,
},
]);
return (
<div
className={active ? "modal-tiket active" : "modal-tiket"}
onClick={() => setActive(false)}
>
<div
className="modal-tiket__content"
onClick={(e) => e.stopPropagation()}
>
<div className="author">
<a href="#">
<img src={creatorMock}></img>
</a>
<a href="#">
<img src={creatorMock}></img>
</a>
</div>
<div className="content">
<h3 className="title-project">
<img src={category} className="title-project__category"></img>
Проект: {tiket.name}
</h3>
<div className="content__task">
<h5>{tiket.code}</h5>
<div className="content__description">
<p>{tiket.descriptions}</p>
<img src={task} className="image-task"></img>
<p>{tiket.descriptions}</p>
</div>
<div className="content__communication">
<p className="comment">
<img src={comments}></img>
<span>{0}</span>
Коментариев
</p>
<p className="file">
<img src={files}></img>
<span>{0}</span>
Файлов
</p>
</div>
</div>
</div>
<div className="workers">
<span>{tiket.code}</span>
<p className="workers__creator">Создатель : {tiket.creator}</p>
<div>
{workers.map((worker, index) => {
return (
<div className="worker" key={index}>
<img src={worker.avatar}></img>
<p>{worker.name}</p>
</div>
);
})}
</div>
<div className="add-worker moreItems">
<button>+</button>
<span>Добавить участников</span>
</div>
<div className="time">
<img src={watch}></img>
<span>Длительность : </span>
<p>{"8:30:22"}</p>
</div>
<button className="start">
Начать делать <img src={arrow}></img>
</button>
</div>
</div>
</div>
);
};
export default ModalTiket;

View File

@ -0,0 +1,199 @@
.modal-tiket {
z-index: 9;
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, 0.11);
position: fixed;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
transform: scale(0);
}
.modal-tiket.active {
transform: scale(1);
}
.modal-tiket__content {
background: #ffffff;
border: 1px solid #dde2e4;
border-radius: 8px;
display: flex;
flex-direction: row;
.author {
padding: 25px 0 0 0;
border-radius: 8px 0 0 8px;
display: flex;
flex-direction: column;
align-items: center;
width: 76px;
background: #fbfbfb;
a {
margin-bottom: 16px;
}
}
.content {
display: flex;
flex-direction: column;
width: 600px;
margin: 26px 0 0 21px;
.title-project {
font-family: "LabGrotesque", sans-serif;
display: flex;
align-items: center;
font-weight: 700;
font-size: 16px;
&__category {
margin-right: 17px;
}
}
&__task {
margin-top: 22px;
padding: 18px;
h5 {
font-family: "Inter", sans-serif;
font-weight: 500;
font-style: normal;
font-size: 16px;
line-height: 24px;
color: #1a1919;
}
}
&__description {
p {
font-weight: 400;
font-size: 14px;
line-height: 140%;
color: #252c32;
text-align: justify;
}
.image-task {
margin: 0 0 20px 0;
}
}
&__communication {
display: flex;
flex-direction: row;
margin: 29px 0 0 -5px;
.comment {
width: 100px;
justify-content: space-evenly;
}
.comment,
.file {
display: flex;
align-items: center;
}
.file {
justify-content: space-between;
margin-left: 20px;
width: 70px;
}
}
}
.workers {
border-left: 1px solid #f1f1f1;
width: 300px;
padding: 40px;
span {
font-family: "Inter", sans-serif;
font-weight: 500;
font-size: 11px;
color: #807777;
}
.add-worker {
display: flex;
align-items: center;
span {
color: #000000;
font-size: 12px;
line-height: 32px;
margin-left: 17px;
font-style: normal;
font-weight: 400;
}
button {
cursor: pointer;
background: #8bcc60;
border-radius: 44px;
width: 33px;
height: 33px;
display: flex;
justify-content: center;
align-items: center;
border: none;
color: white;
font-size: 17px;
}
}
.start {
margin-top: 25px;
width: 151px;
height: 40px;
border: none;
color: white;
background: #1458dd;
border-radius: 44px;
img {
margin-left: 10px;
}
}
.time {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
margin-top: 20px;
width: 160px;
p {
color: #000000;
margin: 0;
}
}
&__creator {
font-size: 14px;
line-height: 32px;
font-weight: 500;
color: #2d4a17;
}
.worker {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 15px;
p {
margin: 0 0 0 11px;
font-size: 12px;
color: #807777;
}
}
}
}

BIN
src/images/arrowStart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

BIN
src/images/avatarMoсk1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
src/images/avatarMoсk2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/images/category.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 15 KiB

3
src/images/search.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path style="fill: black" d="M19.7192 18.3139L16.0114 14.6333C17.4506 12.8374 18.1476 10.5579 17.959 8.26356C17.7705 5.96919 16.7106 3.83432 14.9975 2.29792C13.2844 0.761532 11.0481 -0.0595993 8.74862 0.00337152C6.44911 0.0663423 4.26109 1.00863 2.63448 2.63648C1.00786 4.26433 0.066292 6.454 0.00336896 8.75527C-0.059554 11.0565 0.760954 13.2945 2.29618 15.0089C3.83141 16.7233 5.96466 17.784 8.25729 17.9727C10.5499 18.1614 12.8277 17.4639 14.6222 16.0235L18.3 19.7042C18.3929 19.7979 18.5035 19.8723 18.6253 19.9231C18.747 19.9739 18.8777 20 19.0096 20C19.1415 20 19.2722 19.9739 19.3939 19.9231C19.5157 19.8723 19.6263 19.7979 19.7192 19.7042C19.8993 19.5177 20 19.2684 20 19.009C20 18.7497 19.8993 18.5004 19.7192 18.3139ZM9.01554 16.0235C7.63189 16.0235 6.27932 15.6129 5.12886 14.8436C3.9784 14.0743 3.08172 12.9809 2.55223 11.7016C2.02273 10.4223 1.88419 9.01462 2.15412 7.65653C2.42406 6.29844 3.09035 5.05095 4.06873 4.07183C5.04712 3.0927 6.29366 2.4259 7.65072 2.15576C9.00778 1.88562 10.4144 2.02426 11.6927 2.55417C12.9711 3.08407 14.0637 3.98142 14.8324 5.13276C15.6011 6.28409 16.0114 7.63769 16.0114 9.02239C16.0114 10.8792 15.2743 12.66 13.9623 13.973C12.6504 15.2859 10.871 16.0235 9.01554 16.0235Z" fill="#FF9900"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
src/images/tasksMock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
src/images/watch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 B

View File

@ -1,230 +1,520 @@
import React, {useState} from 'react';
import React, { useState } from "react";
import {ProfileHeader} from "../../components/ProfileHeader/ProfileHeader";
import {Footer} from '../../components/Footer/Footer'
import { ProfileHeader } from "../../components/ProfileHeader/ProfileHeader";
import { Footer } from "../../components/Footer/Footer";
import project from '../../images/trackerProject.svg'
import tasks from '../../images/trackerTasks.svg'
import archive from '../../images/archiveTracker.svg'
import avatarTest from '../../images/AvatarTest .png'
import selectArrow from '../../images/select.svg'
import commentsBoard from '../../images/commentsBoard.svg'
import filesBoard from '../../images/filesBoard.svg'
import ModalTiket from "../../components/UI/ModalTiket/ModalTiket";
import ModalProject from "../../components/UI/ModalProject/ModalProject";
import './tracker.scss'
import project from "../../images/trackerProject.svg";
import tasks from "../../images/trackerTasks.svg";
import archive from "../../images/archiveTracker.svg";
import avatarTest from "../../images/AvatarTest .png";
import selectArrow from "../../images/select.svg";
import commentsBoard from "../../images/commentsBoard.svg";
import filesBoard from "../../images/filesBoard.svg";
import search from "../../images/search.svg"
import "./tracker.scss";
export const Tracker = () => {
const [toggleTab, setToggleTab] = useState(1);
const [projects] = useState([
{
name: "Разработка трекера",
count: 4,
},
{
name: "Кинотеатр",
count: 4,
},
{
name: "Проект страхование",
count: 4,
},
]);
const [tabTaskMok, setTabTaskMok] = useState([
{
name: "Открытые",
open: false,
tasks: [
{
task: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest,
id: 1
},
{
task: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest,
id: 2
},
],
},
{
name: "В процессе",
open: false,
tasks: [
{
task: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest,
id: 3
},
],
},
{
name: "На проверке",
open: false,
tasks: [
{
task: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest,
id: 4
},
{
task: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest,
id: 5
},
{
task: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest,
id: 6
},
{
task: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest,
id: 9
},
],
},
{
name: "Готово",
open: false,
tasks: [
{
task: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest,
id: 7
},
{
task: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest,
id: 8
},
],
},
]);
const [toggleTab, setToggleTab] = useState(1)
const [projects] = useState([
{
name: 'Разработка трекера',
count: 4
},
{
name: 'Кинотеатр',
count: 4
},
{
name: 'Проект страхование',
count: 4
}
])
const [tabTaskMok] = useState([
{
name: 'Открытые',
tasks: [
{
task: 'PR - 2245',
description: 'Сверстать часть таблицы. Сверстать часть таблицы',
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest
},
{
task: 'PR - 2245',
description: 'Сверстать часть таблицы. Сверстать часть таблицы',
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest
}
]
},
{
name: 'В процессе',
tasks: [
{
task: 'PR - 2245',
description: 'Сверстать часть таблицы. Сверстать часть таблицы',
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest
}
]
},
{
name: 'На проверке',
tasks: [
{
task: 'PR - 2245',
description: 'Сверстать часть таблицы. Сверстать часть таблицы',
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest
},
{
task: 'PR - 2245',
description: 'Сверстать часть таблицы. Сверстать часть таблицы',
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest
},
{
task: 'PR - 2245',
description: 'Сверстать часть таблицы. Сверстать часть таблицы',
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest
}
]
},
{
name: 'Готово',
tasks: [
{
task: 'PR - 2245',
description: 'Сверстать часть таблицы. Сверстать часть таблицы',
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest
},
{
task: 'PR - 2245',
description: 'Сверстать часть таблицы. Сверстать часть таблицы',
comments: 12,
files: 0,
avatarCreated: avatarTest,
avatarDo: avatarTest
}
]
}
])
const [completeTasks] = useState([
{
name: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
{
name: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
{
name: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
{
name: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
{
name: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
{
name: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
{
name: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
{
name: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
{
name: "PR - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
{
name: "PK - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
{
name: "PE - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
{
name: "PA - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
{
name: "PB - 2245",
description: "Верстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
{
name: "PC - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
{
name: "PD - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
{
name: "PA - 2245",
description: "Сверстать часть таблицы. Сверстать часть таблицы",
dateComplete: '07/мар/23',
avatarDo: avatarTest,
},
])
const toggleTabs = (index) => {
setToggleTab(index)
const [filterCompleteTasks, setFilterCompleteTasks] = useState(completeTasks)
const [modalActiveTicket, setModalActiveTicket] = useState(false);
const [modalActiveProject, setModalActiveProject] = useState(false);
const [startWrapperIndex, setStartWrapperIndex] = useState(null)
const [wrapperHover, setWrapperHover] = useState([false, false, false, false])
const toggleTabs = (index) => {
setToggleTab(index);
};
function toggleMoreTasks (wrapperIndex) {
setTabTaskMok(prevArray => prevArray.map((elem, index) => {
if (wrapperIndex === index) {
return {...elem, open: !elem.open}
} else {
return elem
}
}))
}
function dragStartHandler(e, task, wrapperIndex) {
setStartWrapperIndex({task: task, index: wrapperIndex})
setTimeout(() => {
e.target.classList.add('tasks__board__item__hide')
},0)
}
function dragEndHandler(e) {
setWrapperHover(prevArray => prevArray.map((elem) => {
return false
}))
e.target.classList.remove('tasks__board__item__hide')
}
function dragOverHandler(e) {
e.preventDefault()
}
function dragEnterHandler(wrapperIndex) {
if (wrapperIndex === startWrapperIndex.index) {
return
}
return (
<div className='tracker'>
<ProfileHeader />
<div className='container'>
<div className='tracker__content'>
<h2 className='tracker__title'>Трекер</h2>
<div className='tracker__tabs'>
<div className='tracker__tabs__head'>
<div className={toggleTab === 1 ? 'tab active-tab' : 'tab'} onClick={() => toggleTabs(1)}>
<img src={project} alt='img' />
<p>Проекты </p>
</div>
<div className={toggleTab === 2 ? 'tab active-tab' : 'tab'} onClick={() => toggleTabs(2)}>
<img src={tasks} alt='img' />
<p>Задачи</p>
</div>
<div className={toggleTab === 3 ? 'tab active-tab' : 'tab'} onClick={() => toggleTabs(3)}>
<img src={archive} alt='img' />
<p>Архив</p>
</div>
</div>
<div className='tracker__tabs__content'>
<div className={toggleTab === 1 ? 'tracker__tabs__content__projects active__content' : 'tracker__tabs__content__projects'}>
{projects.map((project, index) => {
return <div className='project'>
<h3>{project.name}</h3>
<div className='project__info'>
<p>Открытые задачи</p>
<span className='count'>{project.count}</span>
<span className='add'>+</span>
</div>
</div>
})
}
<button><span>+</span>Создать проект</button>
</div>
<div className={toggleTab === 2 ? 'tracker__tabs__content__tasks tasks active__content' : 'tracker__tabs__content__projects'}>
<div className='tasks__head'>
<h4>Проект : Разработка трекера</h4>
<span className='tasks__head__add'>+</span>
<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>+9</span>
</div>
<div className='tasks__head__select'>
<span>Учавствую</span>
<img src={selectArrow} alt='arrow' />
</div>
<div className='tasks__head__select'>
<span>Мои</span>
<img src={selectArrow} alt='arrow' />
</div>
</div>
<div className='tasks__container'>
{tabTaskMok.map((section, index) => {
return <div key={index} className={section.tasks.length >= 3 ? 'tasks__board tasks__board__more' : 'tasks__board'}>
<div className='board__head'>
<span>{section.name}</span>
<div>
<span className='add'>+</span>
<span className='more'>...</span>
</div>
</div>
{section.tasks.map((task, index) => {
return <div key={index} className='tasks__board__item'>
<div className='tasks__board__item__title'>
<p>{task.task}</p>
<span>...</span>
</div>
<p className='tasks__board__item__description'>
{task.description}
</p>
<div className='tasks__board__item__info'>
<div className='tasks__board__item__info__more'>
<img src={commentsBoard} alt='commentsImg' />
<span>{task.comments} коментариев</span>
</div>
<div className='tasks__board__item__info__more'>
<img src={filesBoard} alt='filesImg' />
<span>{task.files} файлов</span>
</div>
<div className='tasks__board__item__info__avatars'>
<img src={task.avatarCreated} alt='avatar' />
<img src={task.avatarDo} alt='avatar' />
</div>
</div>
</div>
})
}
{section.tasks.length >= 3 &&
<span className='moreItems'>+</span>
}
</div>
})
}
</div>
</div>
</div>
</div>
</div>
setWrapperHover(prevArray => prevArray.map((elem, index) => {
if (index === wrapperIndex) {
return true
} else {
return false
}
}))
}
function dragDropHandler(e, wrapperIndex) {
e.preventDefault()
if (startWrapperIndex.index === wrapperIndex) {
return
}
setWrapperHover(prevArray => prevArray.map((elem) => {
return false
}))
setTabTaskMok(prevArray => prevArray.map((elem, index) => {
if (index === wrapperIndex) {
return {...elem, tasks: [...elem.tasks, startWrapperIndex.task]}
} else if (index === startWrapperIndex.index) {
return {...elem, tasks: elem.tasks.filter((item) => {
return item.id !== startWrapperIndex.task.id
}
)}
} else {
return elem
}
}))
}
function filterArchiveTasks(e) {
setFilterCompleteTasks(completeTasks.filter((item) => {
if (!e.target.value) {
return item
}
if (item.name.toLowerCase().startsWith(e.target.value.toLowerCase()) || item.description.toLowerCase().startsWith(e.target.value.toLowerCase())) {
return item
}
}))
}
return (
<div className="tracker">
<ProfileHeader />
<div className="container">
<div className="tracker__content">
<h2 className="tracker__title">Трекер</h2>
<div className="tracker__tabs">
<div className="tracker__tabs__head">
<div
className={toggleTab === 1 ? "tab active-tab" : "tab"}
onClick={() => toggleTabs(1)}
>
<img src={project} alt="img" />
<p>Проекты </p>
</div>
<div
className={toggleTab === 2 ? "tab active-tab" : "tab"}
onClick={() => toggleTabs(2)}
>
<img src={tasks} alt="img" />
<p>Задачи</p>
</div>
<div
className={toggleTab === 3 ? "tab active-tab" : "tab"}
onClick={() => toggleTabs(3)}
>
<img src={archive} alt="img" />
<p>Архив</p>
</div>
</div>
<Footer/>
<div className="tracker__tabs__content">
<div
className={
toggleTab === 1
? "tracker__tabs__content__projects active__content"
: "tracker__tabs__content__projects"
}
>
{projects.map((project, index) => {
return (
<div className="project" key={index}>
<h3>{project.name}</h3>
<div className="project__info">
<p>Открытые задачи</p>
<span className="count">{project.count}</span>
<span className="add">+</span>
</div>
</div>
);
})}
<ModalProject
active={modalActiveProject}
setActive={setModalActiveProject}
/>
<button onClick={() => setModalActiveProject(true)}>
<span>+</span>Создать проект
</button>
</div>
<div
className={
toggleTab === 2
? "tracker__tabs__content__tasks tasks active__content"
: "tracker__tabs__content__projects"
}
>
<div className="tasks__head">
<h4>Проект : Разработка трекера</h4>
<span className="tasks__head__add">+</span>
<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>+9</span>
</div>
<div className="tasks__head__select">
<span>Учавствую</span>
<img src={selectArrow} alt="arrow" />
</div>
<div className="tasks__head__select">
<span>Мои</span>
<img src={selectArrow} alt="arrow" />
</div>
</div>
<ModalTiket
active={modalActiveTicket}
setActive={setModalActiveTicket}
/>
<div className="tasks__container">
{tabTaskMok.map((section, wrapperIndex) => {
return (
<div
key={wrapperIndex}
onDragOver={(e) => dragOverHandler(e)}
onDragEnter={(e) => dragEnterHandler(wrapperIndex)}
onDrop={(e) => dragDropHandler(e, wrapperIndex)}
className={`tasks__board ${section.tasks.length >= 3 ? 'tasks__board__more' : ''} ${wrapperHover[wrapperIndex] ? 'tasks__board__hover' : ''}`}
>
<div className="board__head">
<span className={wrapperIndex === 3 ? "done" : ""}>
{section.name}
</span>
<div>
<span className="add">+</span>
<span className="more">...</span>
</div>
</div>
{section.tasks.map((task, index) => {
if (index > 2) {
if (!section.open) {
return
}
}
return (
<div
key={index}
className="tasks__board__item"
draggable={true}
onDragStart={(e) => dragStartHandler(e, task, wrapperIndex)}
onDragEnd={(e) => dragEndHandler(e)}
onClick={() => setModalActiveTicket(true)}
>
<div className="tasks__board__item__title">
<p>{task.task}</p>
<span>...</span>
</div>
<p className="tasks__board__item__description">
{task.description}
</p>
<div className="tasks__board__item__info">
<div className="tasks__board__item__info__more">
<img src={commentsBoard} alt="commentsImg" />
<span>{task.comments} коментариев</span>
</div>
<div className="tasks__board__item__info__more">
<img src={filesBoard} alt="filesImg" />
<span>{task.files} файлов</span>
</div>
<div className="tasks__board__item__info__avatars">
<img src={task.avatarCreated} alt="avatar" />
<img src={task.avatarDo} alt="avatar" />
</div>
</div>
</div>
);
})}
{section.tasks.length > 3 && (
<span className={section.open ? 'lessItems openItems' : 'moreItems openItems'} onClick={() => toggleMoreTasks(wrapperIndex)}>{section.open ? '-' : '+'}</span>
)}
</div>
);
})}
</div>
</div>
<div
className={
toggleTab === 3
? "tracker__tabs__content__archive tasks active__content"
: "tracker__tabs__content__projects"
}
>
<div className='archive__title'>
<h3>Архив:</h3>
<p>{filterCompleteTasks.length} задач(а)</p>
<div className='archive__search'>
<input type='text' onChange={(event) => filterArchiveTasks(event)} />
<img src={search} alt='search' />
</div>
</div>
<div className='archive__tasksWrapper'>
{filterCompleteTasks.map((task, index) => {
return <div className='archive__completeTask' key={index}>
<div className='archive__completeTask__description'>
<p>{task.description}</p>
<p className='date'>{task.dateComplete}</p>
</div>
<div className='archive__completeTask__info'>
<img src={task.avatarDo} alt='avatar' />
<p>{task.name}</p>
</div>
</div>
})}
</div>
</div>
</div>
</div>
</div>
)
</div>
<Footer />
</div>
);
};

View File

@ -3,6 +3,7 @@
height: 100%;
min-height: 100vh;
font-family: "LabGrotesque", sans-serif;
position: relative;
.container {
max-width: 1160px;
@ -76,6 +77,14 @@
display: none;
align-items: center;
@media (max-width: 785px) {
justify-content: center;
}
@media (max-width: 460px) {
padding: 15px;
}
.project {
width: 48%;
background: #F1F1F1;
@ -83,6 +92,18 @@
padding: 17px 26px 16px;
cursor: pointer;
@media (max-width: 1068px) {
width: 47%;
}
@media (max-width: 785px) {
width: 100%;
}
@media (max-width: 430px) {
padding: 8px 13px 8px;
}
h3 {
font-weight: 700;
font-size: 16px;
@ -96,6 +117,10 @@
align-items: center;
position: relative;
@media (max-width: 430px) {
justify-content: space-between;
}
p {
color: #6F6F6F;
font-weight: 500;
@ -123,6 +148,10 @@
color: #6F6F6F;
font-size: 17px;
margin: 0 25px 0 auto;
@media (max-width: 430px) {
display: none;
}
}
&:after {
@ -132,6 +161,10 @@
color: #6F6F6F;
right: 0;
top: -35%;
@media (max-width: 430px) {
display: none;
}
}
}
}
@ -291,6 +324,12 @@
row-gap: 16px;
height: fit-content;
position: relative;
transition: all 0.3s ease;
&__hover {
box-shadow: 0px 2px 10px #9cc480, 0px 0px 0px 1px rgba(60, 66, 87, 0.08), 0px 1px 1px rgba(0, 0, 0, 0.06);
}
&__item {
padding: 16px;
@ -298,7 +337,11 @@
box-shadow: 0px 3px 2px -2px rgba(0, 0, 0, 0.06), 0px 5px 3px -2px rgba(0, 0, 0, 0.02);
border-radius: 6px;
background: #FFFFFF;
cursor: grabbing;
cursor: pointer;
&__hide {
opacity: 0;
}
&__title {
display: flex;
@ -337,6 +380,7 @@
display: flex;
justify-content: space-between;
align-items: center;
pointer-events: none;
&__more {
cursor: pointer;
@ -362,9 +406,8 @@
}
}
.moreItems {
.openItems {
cursor: pointer;
background: #8BCC60;
border-radius: 44px;
width: 33px;
height: 33px;
@ -373,9 +416,17 @@
align-items: center;
position: absolute;
bottom: -15px;
color: white;
font-size: 20px;
left: 165px;
color: white;
}
.moreItems {
background: #8BCC60;
}
.lessItems {
background: #f92828;
}
&__more {
@ -419,6 +470,136 @@
.active__content {
display: flex;
}
&__archive {
background: white;
border-radius: 12px;
padding: 26px 24px 40px;
min-height: 774px;
display: flex;
flex-direction: column;
.archive {
&__title {
padding-bottom: 8px;
border-bottom: 1px solid #DDE2E4;
display: flex;
align-items: center;
h3 {
color: #111112;
font-weight: 700;
font-size: 16px;
line-height: 32px;
margin-bottom: 0;
}
p {
margin: 0 0 0 10px;
font-weight: 500;
font-size: 14px;
}
}
&__search {
border: 2px solid;
border-color: var(--ds-border-input, #DFE1E6);
margin-left: auto;
padding: 5px;
display: flex;
align-items: center;
input {
outline: none;
border: none;
font-size: 16px;
font-weight: 500;
}
img {
width: 20px;
height: 20px;
}
}
&__tasksWrapper {
margin-top: 10px;
display: flex;
flex-direction: column;
row-gap: 3px;
max-height: 649px;
overflow-y: auto;
margin-right: -16px;
padding-right: 10px;
&::-webkit-scrollbar {
width: 4px;
background: 0 0;
box-shadow: 0 0 14px rgba(0,0,0,.04);
border-radius: 20px;
}
&::-webkit-scrollbar {
width: 4px;
border-radius: 20px;
}
&::-webkit-scrollbar-thumb {
background: #cbd9f9;
border-radius: 20px;
}
&::-webkit-scrollbar-track {
background: #c5c0c6;
border-radius: 20px;
}
}
&__completeTask {
display: flex;
justify-content: space-between;
border: 1px solid var(--ds-border,#dfe1e6);
padding: 8px 10px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
background: var(--ds-background-neutral-subtle-hovered,#f4f5f7);
}
p {
margin-bottom: 0;
}
&__description {
font-size: 14px;
font-weight: 500;
.date {
font-weight: 400;
font-size: 12px;
}
}
&__info {
display: flex;
align-items: center;
img {
width: 36px;
height: 36px;
}
p {
margin-left: 10px;
text-decoration: line-through;
font-size: 11px;
font-weight: 500;
}
}
}
}
}
}
}
}

View File

@ -135,7 +135,7 @@ export const ViewReport = () => {
})}
<tr>
<td></td>
<td><span>Всего: {totalHours} часов</span></td>
<td><span>Всего: {totalHours} {hourOfNum(totalHours)}</span></td>
</tr>
</tbody>
</table>

View File

@ -1,9 +0,0 @@
{
"compilerOptions": {
"target": "es5", // компилируем ts код в js код версии ES5
"module": "esnext",
// для поддержки динамического импорта модулей
"allowSyntheticDefaultImports": true,
"jsx": "react"
}
}