Fixed conflicts

This commit is contained in:
MaxOvs19
2023-05-23 17:15:12 +03:00
66 changed files with 2880 additions and 1689 deletions

View File

@ -7,7 +7,7 @@
padding-right: 54px;
padding-top: 48px;
padding-bottom: 40px;
font-family: 'LabGrotesque', sans-serif;
font-family: "LabGrotesque", sans-serif;
&__header {
display: flex;
@ -56,7 +56,6 @@
}
h3 {
font-size: 2.5em;
font-weight: 400;
font-style: normal;
@ -133,6 +132,8 @@
text-align: center;
}
}
margin-bottom: 60px;
}
&__form {
@ -142,8 +143,8 @@
button {
margin: 0 auto;
//width: 125px;
//height: 42px;
width: 125px;
height: 42px;
padding: 0 5px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 5px;
@ -158,39 +159,34 @@
text-align: center;
a {
display: flex;
align-items: center;
justify-content: center;
width: 115px;
height: 42px;
text-decoration: none;
color: #000000;
color: black;
}
img {
width: 16px;
height: 16px;
margin: 0 10px 0 0;
}
@media (max-width: 1200px) {
width: 90px;
height: 40px;
}
img {
width: 16px;
height: 16px;
margin: 0 10px 0 0;
@media (max-width: 968px) {
width: 62px;
height: 40px;
font-size: 10px;
margin-right: 2px;
img {
margin-right: 2px;
}
}
@media (max-width: 610px) {
img {
@media (max-width: 610px) {
display: none;
}
}
}
@media (max-width: 1200px) {
width: 90px;
height: 40px;
}
@media (max-width: 968px) {
width: 62px;
height: 40px;
font-size: 10px;
@media (max-width: 610px) {
width: auto;
height: auto;
}

View File

@ -1,19 +1,23 @@
import moment from 'moment';
import 'moment/locale/ru';
import moment from "moment";
import "moment/locale/ru";
export function calendarHelper(value) {
const startDay = value.clone().startOf('month').startOf('week').startOf('day');
const endDay = value.clone().endOf('month').endOf('week');
const startDay = value
.clone()
.startOf("month")
.startOf("week")
.startOf("day");
const endDay = value.clone().endOf("month").endOf("week");
const day = startDay.clone().subtract(1, 'day');
const day = startDay.clone().subtract(1, "day");
const calendar = [];
while (day.isBefore(endDay, 'day')) {
while (day.isBefore(endDay, "day")) {
calendar.push(
Array(1)
.fill(0)
.map(() => day.add(1, 'day').clone())
.map(() => day.add(1, "day").clone())
);
}
@ -21,48 +25,84 @@ export function calendarHelper(value) {
}
export function getReports(value) {
const startDay = value.clone().startOf('month').startOf('week').startOf('day');
const reportsStart = `${new Date(startDay).getFullYear()}-${new Date(startDay).getMonth() + 1}-${new Date(startDay).getDate()}`
const endDay = value.clone().endOf('month').endOf('week');
const reportsEnd = `${new Date(endDay).getFullYear()}-${new Date(endDay).getMonth() + 1}-${new Date(endDay).getDate()}`
const getReports = `fromDate=${reportsStart}&toDate=${reportsEnd}`
const startDay = value
.clone()
.startOf("month")
.startOf("week")
.startOf("day");
const reportsStart = `${new Date(startDay).getFullYear()}-${
new Date(startDay).getMonth() + 1
}-${new Date(startDay).getDate()}`;
const endDay = value.clone().endOf("month").endOf("week");
const reportsEnd = `${new Date(endDay).getFullYear()}-${
new Date(endDay).getMonth() + 1
}-${new Date(endDay).getDate()}`;
const getReports = `fromDate=${reportsStart}&toDate=${reportsEnd}`;
return getReports;
}
export function getCreatedDate(day) {
if (day) {
return `${new Date(day).getFullYear()}-${new Date(day).getMonth() + 1}-${new Date(day).getDate()}`
return `${new Date(day).getFullYear()}-${correctDay(
new Date(day).getMonth() + 1
)}-${correctDay(new Date(day).getDate())}`;
} else {
const date = new Date();
const dd = String(date.getDate()).padStart(2, '0');
const mm = String(date.getMonth() + 1).padStart(2, '0');
const dd = String(date.getDate()).padStart(2, "0");
const mm = String(date.getMonth() + 1).padStart(2, "0");
const yyyy = date.getFullYear();
return `${yyyy}-${mm}-${dd}`
return `${yyyy}-${mm}-${dd}`;
}
}
export function correctDay(day) {
if (day < 10) {
return `0${day}`;
}
return day;
}
export function currentMonth() {
const currentMonth = moment().format('MMMM');
const currentMonth = moment().format("MMMM");
return currentMonth.charAt(0).toUpperCase() + currentMonth.slice(1);
}
export function currentMonthAndDay(day) {
return day.format('D MMMM');
return day.format("D MMMM");
}
export function getCorrectDate(day) {
const months = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря']
return `${new Date(day).getDate()} ${months[new Date(day).getMonth()]} ${new Date(day).getFullYear()} года`
};
const months = [
"января",
"февраля",
"марта",
"апреля",
"мая",
"июня",
"июля",
"августа",
"сентября",
"октября",
"ноября",
"декабря",
];
return `${new Date(day).getDate()} ${
months[new Date(day).getMonth()]
} ${new Date(day).getFullYear()} года`;
}
export function currentMonthAndDayReportPage() {
return moment().format('D MMMM');
return moment().format("D MMMM");
}
export function hourOfNum(number) {
const hours = [' час', ' часа', ' часов'];
const hours = [" час", " часа", " часов"];
const cases = [2, 0, 1, 1, 1, 2];
return hours[(number % 100 > 4 && number % 100 < 20) ? 2 : cases[(number % 10 < 5) ? number % 10 : 5]];
return hours[
number % 100 > 4 && number % 100 < 20
? 2
: cases[number % 10 < 5 ? number % 10 : 5]
];
}

View File

@ -3,10 +3,10 @@ import './loader.scss'
import React from "react";
export const Loader = ({width = 50, height = 50}) => {
export const Loader = ({width = 50, height = 50, style}) => {
return (
<div className='loader'>
<SVGLoader type='Circles' color='#fff' height={height} width={width}/>
<SVGLoader type='Circles' color={style ? style : `#fff`} height={height} width={width}/>
</div>
)
};

View File

@ -1,99 +1,127 @@
import React, { useEffect, useState } from 'react'
import {useDispatch, useSelector} from 'react-redux'
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import {getReports} from '../Calendar/calendarHelper'
import {Link, Navigate} from 'react-router-dom'
import { getReports } from "../Calendar/calendarHelper";
import { Link, Navigate } from "react-router-dom";
import moment from "moment";
import {ProfileCalendarComponent} from "./ProfileCalendarComponent";
import {Loader} from "../Loader/Loader";
import {ProfileHeader} from "../ProfileHeader/ProfileHeader";
import {ProfileBreadcrumbs} from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs"
import { Footer } from '../Footer/Footer'
import { ProfileCalendarComponent } from "./ProfileCalendarComponent";
import { Loader } from "../Loader/Loader";
import { ProfileHeader } from "../ProfileHeader/ProfileHeader";
import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs";
import { Footer } from "../Footer/Footer";
import { Navigation } from "../Navigation/Navigation";
import { ViewReport } from "../../pages/ViewReport/ViewReport";
import {urlForLocal} from "../../helper";
import { urlForLocal } from "../../helper";
import {apiRequest} from "../../api/request";
import { getProfileInfo } from '../../redux/outstaffingSlice'
import {getRequestDates, setReportDate, setRequestDate} from "../../redux/reportSlice";
import 'moment/locale/ru'
import './profileCalendar.scss'
import { Navigation } from '../Navigation/Navigation';
import { apiRequest } from "../../api/request";
import { getProfileInfo } from "../../redux/outstaffingSlice";
import {
getRequestDates,
setReportDate,
setRequestDate,
} from "../../redux/reportSlice";
import "moment/locale/ru";
import "./profileCalendar.scss";
export const ProfileCalendar = () => {
if(localStorage.getItem('role_status') === '18') {
return <Navigate to="/profile" replace/>
if (localStorage.getItem("role_status") === "18") {
return <Navigate to="/profile" replace />;
}
const dispatch = useDispatch();
const profileInfo = useSelector(getProfileInfo);
const requestDates = useSelector(getRequestDates);
const [value, setValue] = useState(moment());
const [reports, setReports] = useState([]);
const [totalHours, setTotalHours] = useState(0);
const [loader, setLoader] = useState(true);
function setValueHandler(value) {
setValue(value);
}
useEffect(() => {
dispatch(setRequestDate(getReports(moment())));
}, []);
useEffect(() => {
setLoader(true);
if (!requestDates) {
return;
}
const dispatch = useDispatch();
const profileInfo = useSelector(getProfileInfo)
const requestDates = useSelector(getRequestDates)
const [value, setValue] = useState(moment())
const [reports, setReports] = useState([]);
const [totalHours, setTotalHours] = useState(0);
const [loader, setLoader] = useState(true)
apiRequest(
`/reports/reports-by-date?${requestDates}&user_card_id=${localStorage.getItem(
"cardId"
)}`
).then((reports) => {
let spendTime = 0;
for (const report of reports) {
report.task.map((task) => {
if (task.hours_spent) {
spendTime += Number(task.hours_spent);
}
});
}
setTotalHours(spendTime);
setReports(reports);
setLoader(false);
});
}, [requestDates]);
function setValueHandler (value) {
setValue(value)
}
useEffect(() => {
dispatch(setRequestDate(getReports(moment())))
},[]);
useEffect( () => {
setLoader(true)
if (!requestDates) {
return
}
apiRequest(`/reports/reports-by-date?${requestDates}&user_card_id=${localStorage.getItem('cardId')}`)
.then((reports) => {
let spendTime = 0;
for (const report of reports) {
report.task.map((task) => {
if(task.hours_spent) {
spendTime += Number(task.hours_spent)
}
})
}
setTotalHours(spendTime);
setReports(reports)
setLoader(false)
})
}, [requestDates]);
return (
<div className='profile__calendar'>
<ProfileHeader/>
<Navigation />
<div className='container'>
<ProfileBreadcrumbs links={[{name: 'Главная', link: '/profile'},{name: 'Ваша отчетность', link: '/profile/calendar'}]} />
<h2 className='summary__title'>Ваши отчеты</h2>
<div className='summary__info'>
<div className='summary__person'>
<img src={urlForLocal(profileInfo.photo)} className='summary__avatar' alt='avatar'/>
<p className='summary__name'>{profileInfo.fio}, {profileInfo.specification} разработчик</p>
</div>
<Link to='/report'>
<button className="calendar__btn" onClick={() => {
dispatch(setReportDate(''))
}}>Заполнить отчет за день</button>
</Link>
</div>
{loader ?
<div className='loader__wrapper'>
<Loader height={80} width={80} />
</div>
:
<div className='row calendar__wrapper'>
<div className='col-12 col-xl-12'>
<ProfileCalendarComponent setValueHandler={setValueHandler} value={value} reports={reports} totalHours={totalHours} />
</div>
</div>
}
</div>
<Footer />
return (
<div className="profile__calendar">
<ProfileHeader />
<Navigation />
<div className="container">
<ProfileBreadcrumbs
links={[
{ name: "Главная", link: "/profile" },
{ name: "Ваша отчетность", link: "/profile/calendar" },
]}
/>
<h2 className="summary__title">Ваши отчеты</h2>
<div className="summary__info">
<div className="summary__person">
<img
src={urlForLocal(profileInfo.photo)}
className="summary__avatar"
alt="avatar"
/>
<p className="summary__name">
{profileInfo.fio}, {profileInfo.specification} разработчик
</p>
</div>
<Link to="/report">
<button
className="calendar__btn"
onClick={() => {
dispatch(setReportDate(""));
}}
>
Заполнить отчет за день
</button>
</Link>
</div>
)
{loader ? (
<div className="loader__wrapper">
<Loader height={80} width={80} />
</div>
) : (
<div className="row calendar__wrapper">
<div className="col-12 col-xl-12">
<ProfileCalendarComponent
setValueHandler={setValueHandler}
value={value}
reports={reports}
totalHours={totalHours}
/>
</div>
</div>
)}
</div>
<Footer />
</div>
);
};

View File

@ -1,141 +1,175 @@
import React, { useState, useEffect } from 'react'
import arrow from '../../images/arrowCalendar.png'
import rectangle from '../../images/rectangle__calendar.png'
import calendarIcon from '../../images/calendar_icon.png'
import moment from 'moment'
import {calendarHelper, currentMonthAndDay, getReports, hourOfNum} from '../Calendar/calendarHelper'
import {setReportDate, setRequestDate} from '../../redux/reportSlice';
import {useDispatch} from "react-redux";
import {Link} from "react-router-dom";
import React, { useState, useEffect } from "react";
import arrow from "../../images/arrowCalendar.png";
import rectangle from "../../images/rectangle__calendar.png";
import calendarIcon from "../../images/calendar_icon.png";
import moment from "moment";
import {
calendarHelper,
currentMonthAndDay,
getReports,
hourOfNum,
} from "../Calendar/calendarHelper";
import {
setReportDate,
setRequestDate,
setSendRequest,
} from "../../redux/reportSlice";
import { useDispatch } from "react-redux";
import { Link } from "react-router-dom";
import ShortReport from "../ShortReport/ShortReport";
import 'moment/locale/ru'
import './../Calendar/calendarComponent.scss'
import "moment/locale/ru";
import "./../Calendar/calendarComponent.scss";
export const ProfileCalendarComponent = React.memo(({value, setValueHandler, reports, totalHours}) => {
export const ProfileCalendarComponent = React.memo(
({ value, setValueHandler, reports, totalHours }) => {
const dispatch = useDispatch();
const [currentDay] = useState(moment())
const [calendar, setCalendar] = useState([])
const [month, setMonth] = useState('');
const [currentDay] = useState(moment());
const [calendar, setCalendar] = useState([]);
const [month, setMonth] = useState("");
const [shortReport, setShortReport] = useState(false);
useEffect(() => {
setCalendar(calendarHelper(value))
}, [value])
setCalendar(calendarHelper(value));
}, [value]);
useEffect(() => {
setMonth(value.format('MMMM'))
setMonth(value.format("MMMM"));
}, [month]);
function isToday(day) {
return day.isSame(new Date(), 'day')
return day.isSame(new Date(), "day");
}
function correctDay(day) {
if (day < 10) {
return `0${day}`
} return day
if (day < 10) {
return `0${day}`;
}
return day;
}
function dayStyles(day) {
if (currentDay < day) return `block`
for (const date of reports) {
if (`${new Date(day).getFullYear()}-${correctDay(new Date(day).getMonth() + 1)}-${correctDay(new Date(day).getDate())}` === date.created_at) {
return `before`
}
if (currentDay < day) return `block`;
for (const date of reports) {
if (
`${new Date(day).getFullYear()}-${correctDay(
new Date(day).getMonth() + 1
)}-${correctDay(new Date(day).getDate())}` === date.created_at
) {
return `before`;
}
if (day.day() === 6 || day.day() === 0) return `selected`
if (isToday(day)) return `today`
return 'pass'
}
if (day.day() === 6 || day.day() === 0) return `selected`;
if (isToday(day)) return `today`;
return "pass";
}
function correctRoute(day) {
for (const date of reports) {
if (`${new Date(day).getFullYear()}-${correctDay(new Date(day).getMonth() + 1)}-${correctDay(new Date(day).getDate())}` === date.created_at) {
return `../view`
}
for (const date of reports) {
if (
`${new Date(day).getFullYear()}-${correctDay(
new Date(day).getMonth() + 1
)}-${correctDay(new Date(day).getDate())}` === date.created_at
) {
return;
}
return '../../report'
}
return "../../report";
}
function prevMonth() {
return value.clone().subtract(1, 'month')
return value.clone().subtract(1, "month");
}
function nextMonth() {
return value.clone().add(1, 'month');
return value.clone().add(1, "month");
}
return (
<div className='calendar-component'>
<div className='calendar-component__header'>
<div className='calendar-component__header-info'>
<h3>Мои отчеты:</h3>
<p className='calendar__hours'>
{month}&nbsp;<span>{totalHours} {hourOfNum(totalHours)} </span>
</p>
</div>
<div className='calendar-component__header-switcher'>
<div className='calendar-component__header-box' onClick={() => {
setValueHandler(prevMonth())
dispatch(setRequestDate(getReports(prevMonth())))
}}>
<img src={arrow} alt='' />
<span>
{prevMonth().format('MMMM')}
</span>
</div>
<div className='calendar-component__header-box'>
<span>{value.format('YYYY')}</span>
</div>
<div className='calendar-component__header-box' onClick={() => {
setValueHandler(nextMonth())
dispatch(setRequestDate(getReports(nextMonth())))
}}>
<span>
{nextMonth().format('MMMM')}
</span>
<img src={arrow} alt='' />
</div>
</div>
<div className="calendar-component">
<div className="calendar-component__header">
<div className="calendar-component__header-info">
<h3>Мои отчеты:</h3>
<p className="calendar__hours">
{month}&nbsp;
<span>
{totalHours} {hourOfNum(totalHours)}{" "}
</span>
</p>
</div>
<div className="calendar-component__header-switcher">
<div
className="calendar-component__header-box"
onClick={() => {
setValueHandler(prevMonth());
dispatch(setRequestDate(getReports(prevMonth())));
}}
>
<img src={arrow} alt="" />
<span>{prevMonth().format("MMMM")}</span>
</div>
<div className='calendar-component__rectangle'>
<img src={rectangle} alt='' />
<div className="calendar-component__header-box">
<span>{value.format("YYYY")}</span>
</div>
<div className='calendar-component__body'>
<div>
<p>Пн</p>
<p>Вт</p>
<p>Ср</p>
<p>Чт</p>
<p>Пт</p>
<p>Сб</p>
<p>Вс</p>
</div>
<div className='calendar-component__form'>
{calendar.map((week) =>
week.map((day) => (
<button
onClick={() => {
dispatch(setReportDate(day))
}}
key={day}
className={dayStyles(day)}
name={day.format('dddd')}
id='btn'
>
<Link to={correctRoute(day)}>
<img className={'calendar__icon'} src={calendarIcon} alt='' />
{currentMonthAndDay(day)}
</Link>
</button>
))
)}
</div>
<div
className="calendar-component__header-box"
onClick={() => {
setValueHandler(nextMonth());
dispatch(setRequestDate(getReports(nextMonth())));
}}
>
<span>{nextMonth().format("MMMM")}</span>
<img src={arrow} alt="" />
</div>
</div>
</div>
)
})
<div className="calendar-component__rectangle">
<img src={rectangle} alt="" />
</div>
<div className="calendar-component__body">
<div>
<p>Пн</p>
<p>Вт</p>
<p>Ср</p>
<p>Чт</p>
<p>Пт</p>
<p>Сб</p>
<p>Вс</p>
</div>
<div className="calendar-component__form">
{calendar.map((week) =>
week.map((day) => (
<button
onClick={() => {
dispatch(setReportDate(day));
setShortReport(true);
dispatch(setSendRequest(true));
}}
key={day}
className={dayStyles(day)}
name={day.format("dddd")}
id="btn"
>
<Link to={correctRoute(day)}>
<img
className={"calendar__icon"}
src={calendarIcon}
alt=""
/>
{currentMonthAndDay(day)}
</Link>
</button>
))
)}
</div>
</div>
{shortReport && <ShortReport />}
</div>
);
}
);

View File

@ -1,5 +1,5 @@
.profile__calendar {
background: #F1F1F1;
background: #f1f1f1;
height: 100%;
min-height: 100vh;
font-family: "LabGrotesque", sans-serif;
@ -32,7 +32,6 @@
}
}
.calendar__wrapper {
min-height: 719px;

View File

@ -1,69 +0,0 @@
import React, { useEffect, useState } from "react";
import ModalSettings from "../UI/ModalSettings/ModalSettings";
import link from "../../images/link.svg";
import archiveSet from "../../images/archive.svg";
import del from "../../images/delete.svg";
import edit from "../../images/edit.svg";
import "./projectTiket.scss";
export const ProjectTiket = ({ project, index, setOpenProject }) => {
const [modalSettings, setModalSettings] = useState(false);
useEffect(() => {
initListeners();
}, []);
function initListeners() {
document.addEventListener("click", closeByClickingOut);
}
function closeByClickingOut(event) {
const path = event.path || (event.composedPath && event.composedPath());
if (
event &&
!path.find((item) => item.classList && item.classList.contains("project"))
) {
setModalSettings(false);
}
}
return (
<div className="project" key={index}>
<h3 onClick={() => setOpenProject(true)}>{project.name}</h3>
<div className="project__info">
<p>Открытые задачи</p>
<span className="count">{project.count}</span>
<span className="add">+</span>
<span className="menu-settings" onClick={() => setModalSettings(true)}>
...
</span>
</div>
<ModalSettings active={modalSettings}>
<div className="project__settings-menu">
<div>
<img src={edit}></img>
<p>редактировать</p>
</div>
<div>
<img src={link}></img>
<p>ссылка на проект</p>
</div>
<div>
<img src={archiveSet}></img>
<p>в архив</p>
</div>
<div>
<img src={del}></img>
<p>удалить</p>
</div>
</div>
</ModalSettings>
</div>
);
};
export default ProjectTiket;

View File

@ -0,0 +1,107 @@
import React, { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { Link } from "react-router-dom";
import { apiRequest } from "../../api/request";
import { deleteProject, modalToggle } from "../../redux/projectsTrackerSlice";
import { ModalSelect } from "../UI/ModalSelect/ModalSelect";
import TrackerModal from "../UI/TrackerModal/TrackerModal";
import link from "../../images/link.svg";
import archiveSet from "../../images/archive.svg";
import del from "../../images/delete.svg";
import edit from "../../images/edit.svg";
import "./projectTiket.scss";
export const ProjectTiket = ({ project, index }) => {
const [modalSelect, setModalSelect] = useState(false);
const [modalAdd, setModalAdd] = useState(false);
const dispatch = useDispatch();
useEffect(() => {
initListeners();
}, []);
function initListeners() {
document.addEventListener("click", closeByClickingOut);
}
function closeByClickingOut(event) {
const path = event.path || (event.composedPath && event.composedPath());
if (
event &&
!path.find((item) => item.classList && item.classList.contains("project"))
) {
setModalSelect(false);
}
}
function removeProject() {
apiRequest("/project/update", {
method: "PUT",
data: {
project_id: project.id,
status: 10,
},
}).then((res) => {
dispatch(deleteProject(project));
});
}
return (
<div className="project" key={index}>
<Link to={`/tracker/project/${project.id}`}>{project.name}</Link>
<div className="project__info">
<p>Открытые задачи</p>
<span className="count">
{project.columns.reduce(
(accumulator, currentValue) =>
accumulator + currentValue.tasks.length,
0
)}
</span>
<span className="menu-settings" onClick={() => setModalSelect(true)}>
...
</span>
</div>
<TrackerModal
active={modalAdd}
setActive={setModalAdd}
defautlInput={project.name}
projectId={project.id}
></TrackerModal>
<ModalSelect active={modalSelect}>
<div className="project__settings-menu">
<div
onClick={() => {
dispatch(modalToggle("editProject"));
setModalAdd(true);
setModalSelect(false);
}}
>
<img src={edit}></img>
<p>редактировать</p>
</div>
<div>
<img src={link}></img>
<p>ссылка на проект</p>
</div>
<div>
<img src={archiveSet}></img>
<p>в архив</p>
</div>
<div onClick={removeProject}>
<img src={del}></img>
<p>удалить</p>
</div>
</div>
</ModalSelect>
</div>
);
};
export default ProjectTiket;

View File

@ -19,7 +19,7 @@
padding: 8px 13px 8px;
}
h3 {
a {
font-weight: 700;
font-size: 18px;
line-height: 32px;
@ -27,7 +27,12 @@
margin-bottom: 10px;
overflow: hidden;
white-space: nowrap;
display: flex;
text-overflow: ellipsis;
&:hover {
color: black;
}
}
&__info {
@ -78,7 +83,6 @@
color: #6f6f6f;
right: 0;
top: -35%;
z-index: 999;
@media (max-width: 430px) {
display: none;

View File

@ -0,0 +1,176 @@
import React, { useEffect, useState } from "react";
import { apiRequest } from "../../api/request";
import {
getCorrectDate,
getCreatedDate,
hourOfNum,
} from "../../components/Calendar/calendarHelper";
import {
getReportDate,
getSendRequest,
setSendRequest,
} from "../../redux/reportSlice";
import { useDispatch, useSelector } from "react-redux";
import { Link } from "react-router-dom";
import { Loader } from "../Loader/Loader";
import "./shortReport.scss";
export const ShortReport = ({}) => {
const reportDate = useSelector(getReportDate);
const sendReport = useSelector(getSendRequest);
const dispatch = useDispatch();
const [taskText, setTaskText] = useState([]);
const [difficulties, setDifficulties] = useState([]);
const [tomorrowTask, setTomorrowTask] = useState([]);
const [totalHours, setTotalHours] = useState(0);
const [loader, setLoader] = useState(false);
const [dateCreate, setDateCreate] = useState("");
function getReportFromDate(day) {
setLoader(true);
setTaskText([]);
setDifficulties([]);
setTomorrowTask([]);
setTotalHours(0);
apiRequest(
`reports/find-by-date?user_card_id=${localStorage.getItem(
"cardId"
)}&date=${day}`
).then((res) => {
let spendTime = 0;
for (const item of res) {
setDateCreate(item.created_at);
if (item.difficulties) {
setDifficulties((prevArray) => [...prevArray, item.difficulties]);
}
if (item.tomorrow) {
setTomorrowTask((prevArray) => [...prevArray, item.tomorrow]);
}
item.task.map((task) => {
const taskInfo = {
hours: task.hours_spent,
task: task.task,
id: task.id,
};
if (task.hours_spent) {
spendTime += Number(task.hours_spent);
}
setTaskText((prevArray) => [...prevArray, taskInfo]);
});
}
setTotalHours(spendTime);
setLoader(false);
});
}
if (sendReport) {
dispatch(setSendRequest(false));
getReportFromDate(getCreatedDate(reportDate));
}
return (
<div>
<div className="viewReport__info short-report">
<div className="viewReport__title-box">
<h2 className="viewReport__title">
Ваши отчеты - <span>просмотр отчета за день</span>
</h2>
<Link to={`../view/${dateCreate}`}>
Посмотреть подробнее об отчете
</Link>
</div>
<div className="viewReport__bar">
<h3 className="viewReport__bar__date">
{getCorrectDate(reportDate)}
</h3>
<p className="viewReport__bar__hours">
Вами потрачено на работу :{" "}
<span>
{totalHours} {hourOfNum(totalHours)}
</span>
</p>
</div>
</div>
{loader && <Loader style="green" />}
{Boolean(taskText.length) && (
<div className="viewReport__content">
<div className="table__container">
<table className="viewReport__done">
<thead>
<tr>
<th>
<p>Какие задачи были выполнены?</p>
</th>
<th>
<p>Время</p>
</th>
</tr>
</thead>
<tbody>
{taskText.length &&
taskText.map((task, index) => {
return (
<tr key={task.id}>
<td>
<p>
{index + 1}. {task.task}
</p>
</td>
<td>
<div className="viewReport__done__hours__item">
<span>{task.hours}</span>
<p className="hours">
{hourOfNum(task.hours)} на задачу
</p>
</div>
</td>
</tr>
);
})}
<tr>
<td></td>
<td>
<span>
Всего: {totalHours} {hourOfNum(totalHours)}
</span>
</td>
</tr>
</tbody>
</table>
</div>
{Boolean(difficulties.length) && (
<div className="viewReport__item">
<h3>Какие сложности возникли?</h3>
{difficulties.map((item, index) => {
return <p key={index}>{item}</p>;
})}
</div>
)}
{Boolean(tomorrowTask.length) && (
<div className="viewReport__item">
<h3>Что планируется сделать завтра?</h3>
{tomorrowTask.map((item, index) => {
return <p key={index}>{item}</p>;
})}
</div>
)}
</div>
)}
{!Boolean(taskText.length) && !loader && (
<div className="viewReport__noTask">
<p>
В этот день вы <span>не заполняли</span> отчет
</p>
</div>
)}
</div>
);
};
export default ShortReport;

View File

@ -0,0 +1,15 @@
.short-report {
margin-bottom: 30px;
}
.viewReport {
&__title-box {
display: flex;
align-items: center;
justify-content: space-between;
a {
font-size: 16px;
}
}
}

View File

@ -1,11 +1,10 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
import arrow from "../../images/sideBarArrow.svg";
import LogoITguild from "../../images/LogoITguild.svg";
import "./sidebar.scss";
import { Link } from "react-router-dom";
import { FREQUENTLY_ASKED_QUESTIONS_ROUTE } from "../../constants/router-path";
export const SideBar = () => {
const [active, setActive] = useState(false);
@ -48,10 +47,10 @@ export const SideBar = () => {
</div>
<ul className="auth-body__navigation">
<li>
<a href="#">Вход для партнеров</a>
<Link to={"/auth"}>Вход для партнеров</Link>
</li>
<li>
<a href="#">Кабинет разработчика</a>
<Link to={"/auth"}>Кабинет разработчика</Link>
</li>
<li>
<a href="#">Школа</a>
@ -63,10 +62,10 @@ export const SideBar = () => {
<a href="#">Контакты</a>
</li>
<li>
<a href="#">Блог</a>
<Link to={"/blog"}>Блог</Link>
</li>
<li>
<Link to={FREQUENTLY_ASKED_QUESTIONS_ROUTE}>FAQ</Link>
<Link to={"/frequently-asked-questions"}>FAQ</Link>
</li>
</ul>
<p className="auth-body__politic">Политика конфиденциальности</p>

View File

@ -20,11 +20,13 @@
}
.auth-title {
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
height: 80%;
z-index: 9999;
.text {
display: flex;
@ -108,8 +110,10 @@
.auth-body {
padding: 40px;
display: none;
z-index: -1;
visibility: hidden;
transition: 0.2s ease-in-out;
opacity: 0;
z-index: 99;
position: absolute;
top: 0;
left: 0;
@ -173,6 +177,9 @@
}
.auth-body.active {
visibility: visible;
transition: 0.1s ease-in-out;
opacity: 1;
display: flex;
width: 424px;
left: 140px;
@ -184,6 +191,6 @@
@media (max-width: 1375px) {
left: 0;
width: 100%;
height: 535px;
height: 605px;
}
}

View File

@ -0,0 +1,22 @@
import React from "react";
import cardCalendar from "../../../images/cardCalendar.svg";
import { Link } from "react-router-dom";
import "./cardArticle.scss";
export const CardArticle = ({ images, title, data, id }) => {
return (
<div className="card-article">
<Link to={`/blog/article/${id}`}>
<img src={images} />
<h5>{title}</h5>
<div className="card-article__data">
<img src={cardCalendar} />
<p>{data}</p>
</div>
</Link>
</div>
);
};
export default CardArticle;

View File

@ -0,0 +1,36 @@
.card-article {
background: #ffffff;
border-radius: 12px;
width: 344px;
margin-bottom: 24px;
transition: 0.4s;
a {
color: #000000;
}
&:hover {
cursor: pointer;
transform: scale(1.05);
transition: 0.4s;
a {
color: #000000;
}
}
h5 {
font-style: normal;
font-weight: 500;
font-size: 17px;
line-height: 20px;
padding: 12px 16px 8px 16px;
}
&__data {
display: flex;
padding: 0 0 14px 16px;
img {
margin-right: 9px;
}
}
}

View File

@ -175,7 +175,6 @@
.auth-body.active {
left: 0;
width: 100%;
height: 535px;
}
.auth-body {

View File

@ -1,13 +1,13 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
import ModalLayout from "../ModalLayout/ModalLayout";
import avatar from "../../../images/mokPerson.png";
import logoTg from "../../../images/TgLogo.svg";
import arrow from "../../../images/right-arrow.png";
import interview from "../../../images/interviewLogo.svg";
import { Link } from "react-router-dom";
import ModalAdd from "../ModalAdd/ModalAdd";
import "./modalAspt.scss";
export const ModalAspt = ({ active, setActive, level }) => {
@ -82,7 +82,8 @@ export const ModalAspt = ({ active, setActive, level }) => {
</div>
<span className="exit" onClick={() => setActive(false)}></span>
</div>
<ModalAdd active={modalSend} setActive={setModalSend}>
<ModalLayout active={modalSend} setActive={setModalSend}>
<div className="send">
<img src={interview}></img>
<h2>Спасибо, собеседование назначено</h2>
@ -93,7 +94,7 @@ export const ModalAspt = ({ active, setActive, level }) => {
Время собеседования: <span>{time}</span>
</p>
</div>
</ModalAdd>
</ModalLayout>
</div>
);
};

View File

@ -1,53 +0,0 @@
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { setProject } from "../../../redux/projectsTrackerSlice";
import "./ModalCreate.scss";
export const ModalCreate = ({ active, setActive, title }) => {
const [inputValue, setInputValue] = useState("");
const dispatch = useDispatch();
function createName() {
if (inputValue === "") {
return;
} else {
let newItem = {
name: inputValue,
count: 0,
};
dispatch(setProject(newItem));
setActive(false);
setInputValue("");
}
}
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>{title}</h4>
<div className="input-container">
<input
className="name-project"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
></input>
</div>
<button className="create-project" onClick={createName}>
Создать
</button>
</div>
<span className="exit" onClick={() => setActive(false)}></span>
</div>
</div>
);
};
export default ModalCreate;

View File

@ -1,77 +0,0 @@
.modal-project {
z-index: 9999;
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);
&__content {
position: relative;
width: 424px;
height: 248px;
background: linear-gradient(180deg, #ffffff 0%, #ebebeb 100%);
border-radius: 40px;
padding: 15px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.title-project {
display: flex;
align-items: flex-start;
flex-direction: column;
.input-container {
width: 287px;
height: 35px;
background: #ffffff;
border-radius: 8px;
}
h4 {
font-weight: 500;
font-size: 22px;
line-height: 26px;
color: #263238;
margin-bottom: 22px;
}
}
.name-project {
margin-left: 10px;
border: none;
outline: none;
height: 100%;
width: 90%;
font-size: 14px;
}
.create-project {
margin: 30px 0 0 0;
width: 130px;
height: 37px;
background: #52b709;
border-radius: 44px;
border: none;
font-weight: 400;
font-size: 15px;
line-height: 32px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
}
}
.modal-project.active {
transform: scale(1);
}

View File

@ -1,8 +1,6 @@
import React from "react";
import "./modalAdd.scss";
export const ModalAdd = ({ children, active, setActive }) => {
export const ModalLayout = ({ active, setActive, children }) => {
return (
<div
className={active ? "modal-add active" : "modal-add"}
@ -10,10 +8,9 @@ export const ModalAdd = ({ children, active, setActive }) => {
>
<div className="modal-add__content" onClick={(e) => e.stopPropagation()}>
{children}
<span className="exit" onClick={() => setActive(false)}></span>
</div>
</div>
);
};
export default ModalAdd;
export default ModalLayout;

View File

@ -0,0 +1,14 @@
import React from "react";
import "./modalSelect.scss";
export const ModalSelect = ({ active, children }) => {
return (
<div className={active ? "project__settings active" : "project__settings "}>
<span className="project__settings-ellipsis">...</span>
{children}
</div>
);
};
export default ModalSelect;

View File

@ -21,6 +21,14 @@
}
}
}
&-ellipsis {
position: absolute;
top: -2px;
left: 10px;
font-size: 21px;
color: #6f6f6f;
}
}
&__settings.active {

View File

@ -1,13 +0,0 @@
import React from "react";
import "./modalSettings.scss";
export const ModalSettings = ({ active, children }) => {
return (
<div className={active ? "project__settings active" : "project__settings "}>
{children}
</div>
);
};
export default ModalSettings;

View File

@ -1,11 +1,17 @@
import React, { useState } from "react";
import React, {useEffect, useState} from "react";
import { Link } from "react-router-dom";
import TrackerModal from "../TrackerModal/TrackerModal";
import { apiRequest } from "../../../api/request";
import { useDispatch } from "react-redux";
import {
modalToggle,
setProjectBoardFetch,
} from "../../../redux/projectsTrackerSlice";
import avatarMock1 from "../../../images/avatarMoсk1.png";
import avatarMock2 from "../../../images/avatarMoсk2.png";
import category from "../../../images/category.png";
import watch from "../../../images/watch.png";
import file from "../../../images/fileModal.svg";
import task from "../../../images/tasksMock.png";
import taskImg from "../../../images/tasksMock.png";
import arrow from "../../../images/arrowStart.png";
import link from "../../../images/link.svg";
import archive from "../../../images/archive.svg";
@ -15,29 +21,45 @@ import send from "../../../images/send.svg";
import plus from "../../../images/plus.svg";
import fullScreen from "../../../images/inFullScreen.svg";
import ModalAdd from "../ModalAdd/ModalAdd";
import "./ModalTicket.scss";
import { Link } from "react-router-dom";
export const ModalTiсket = ({ active, setActive, index }) => {
const [tiket] = useState({
name: "Разработка трекера",
code: "PR - 2245",
creator: "Василий Тарасов",
descriptions:
"На многих страницах сайта отсутствуют или некорректно заполнены метатеги Description. Это может негативно повлиять на представление сайта в результатах поиска. Необходимо исправить все страницы где есть ошибки или отсутствует Title и Description.",
});
const [workers] = useState([
{
name: "Дмитрий Рогов",
avatar: avatarMock2,
},
{
name: "Марина Серова",
avatar: avatarMock1,
},
]);
export const ModalTiсket = ({
active,
setActive,
task,
projectId,
projectName,
}) => {
const dispatch = useDispatch();
const [addSubtask, setAddSubtask] = useState(false);
const [editOpen, setEditOpen] = useState(false);
const [inputsValue, setInputsValue] = useState({title: task.title, description: task.description})
function deleteTask() {
apiRequest("/task/update-task", {
method: "PUT",
data: {
task_id: task.id,
status: 0,
},
}).then((res) => {
setActive(false);
dispatch(setProjectBoardFetch(projectId));
});
}
function editTask() {
apiRequest("/task/update-task", {
method: "PUT",
data: {
task_id: task.id,
title: inputsValue.title,
description: inputsValue.description
},
}).then((res) => {
dispatch(setProjectBoardFetch(projectId));
});
}
return (
<div
@ -51,23 +73,34 @@ export const ModalTiсket = ({ active, setActive, index }) => {
<div className="content">
<h3 className="title-project">
<img src={category} className="title-project__category"></img>
Проект: {tiket.name}
<Link to={`/tracker/${index}`} className="title-project__full">
Проект: {projectName}
<Link
to={`/tracker/task/${task.id}`}
className="title-project__full"
>
<img src={fullScreen}></img>
</Link>
</h3>
<div className="content__task">
<span>Задача</span>
<h5>{tiket.code}</h5>
{editOpen ? <input value={inputsValue.title} onChange={(e) => {
setInputsValue((prevValue) => ({...prevValue, title: e.target.value}))
}} /> :<h5>{inputsValue.title}</h5>}
<div className="content__description">
<p>{tiket.descriptions}</p>
<img src={task} className="image-task"></img>
<p>{tiket.descriptions}</p>
{editOpen ? <input value={inputsValue.description} onChange={(e) => {
setInputsValue((prevValue) => ({...prevValue, description: e.target.value}))
}}/> :<p>{inputsValue.description}</p>}
<img src={taskImg} className="image-task"></img>
</div>
<div className="content__communication">
<p className="tasks">
<button onClick={() => setAddSubtask(true)}>
<button
onClick={() => {
dispatch(modalToggle("addSubtask"));
setAddSubtask(true);
}}
>
<img src={plus}></img>
Добавить под задачу
</button>
@ -90,17 +123,18 @@ export const ModalTiсket = ({ active, setActive, index }) => {
<div className="workers">
<div className="workers_box">
<span className="exit" onClick={() => setActive(false)}></span>
<span>{tiket.code}</span>
<p className="workers__creator">Создатель : {tiket.creator}</p>
<span>{task.title}</span>
<p className="workers__creator">Создатель : {task.user?.fio}</p>
<div>
{workers.map((worker, index) => {
return (
<div className="worker" key={index}>
<img src={worker.avatar}></img>
<p>{worker.name}</p>
</div>
);
})}
{Boolean(task.taskUsers?.length) &&
task.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">
@ -122,39 +156,38 @@ export const ModalTiсket = ({ active, setActive, index }) => {
</div>
<div className="workers_box-bottom">
<div>
<div className={editOpen ? 'edit' : ''} onClick={() => {
if(editOpen) {
setEditOpen(!editOpen)
editTask()
} else {
setEditOpen(!editOpen)
}
}}>
<img src={edit}></img>
<p>редактировать</p>
<p>{editOpen ? 'сохранить' : 'редактировать'}</p>
</div>
<div>
<img src={link}></img>
<p>ссылка на проект</p>
</div>
<div>
<div onClick={deleteTask}>
<img src={archive}></img>
<p>в архив</p>
</div>
<div>
<div onClick={deleteTask}>
<img src={del}></img>
<p>удалить</p>
</div>
</div>
</div>
</div>
<ModalAdd active={addSubtask} setActive={setAddSubtask}>
<div className="title-project subtask">
<h4>
Вы добавляете подзадачу <p>в колонку задачи {"Готово"}</p>
</h4>
<p className="title-project__decs">Введите текст</p>
<div>
<textarea className="title-project__textarea"></textarea>
</div>
</div>
<button className="button-add" onClick={(e) => e.preventDefault()}>
Добавить
</button>
</ModalAdd>
<TrackerModal
active={addSubtask}
setActive={setAddSubtask}
defautlInput={task.column_id}
></TrackerModal>
</div>
);
};

View File

@ -6,19 +6,18 @@
position: fixed;
top: 0;
left: 0;
display: flex;
display: none;
align-items: center;
justify-content: center;
transform: scale(0);
}
.modal-tiket.active {
transform: scale(1);
display: flex;
}
.modal-tiket__content {
background: #ffffff;
border: 1px solid #dde2e4;
//border: 1px solid #dde2e4;
border-radius: 8px;
display: flex;
flex-direction: row;
@ -50,6 +49,16 @@
&__task {
margin-top: -5px;
padding: 18px;
display: flex;
flex-direction: column;
input {
font-style: normal;
font-size: 16px;
line-height: 24px;
max-width: 340px;
outline: none;
}
button {
img {
@ -64,10 +73,14 @@
font-size: 16px;
line-height: 24px;
color: #1a1919;
margin-bottom: 0;
}
}
&__description {
display: flex;
flex-direction: column;
margin-top: 10px;
p {
font-weight: 400;
font-size: 14px;
@ -77,7 +90,8 @@
}
.image-task {
margin: 0 0 20px 0;
margin: 10px 0 20px 0;
max-width: 330px;
}
}
@ -266,6 +280,19 @@
line-height: 32px;
font-weight: 500;
color: #2d4a17;
display: flex;
max-width: 200px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
span {
margin-left: 5px;
font-size: 14px;
line-height: 32px;
font-weight: 500;
display: flex;
}
}
.worker {
@ -291,19 +318,30 @@
}
&-bottom {
padding: 40px 0 75px 56px;
padding: 40px 110px 75px 56px;
font-weight: 400;
font-size: 14px;
line-height: 38px;
div {
display: flex;
cursor: pointer;
align-items: center;
padding-left: 10px;
p {
margin: 0 0 0 12px;
}
}
.edit {
background: #52b709;
border-radius: 50px;
p {
font-weight: 700;
}
}
}
}
}

View File

@ -1,13 +1,17 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import { ProfileHeader } from "../../ProfileHeader/ProfileHeader";
import { ProfileBreadcrumbs } from "../../ProfileBreadcrumbs/ProfileBreadcrumbs";
import { Footer } from "../../Footer/Footer";
import { Link } from "react-router-dom";
import ModalAdd from "../ModalAdd/ModalAdd";
import { Link, useParams, useNavigate } from "react-router-dom";
import TrackerModal from "../TrackerModal/TrackerModal";
import { Navigation } from "../../Navigation/Navigation";
import {Loader} from "../../Loader/Loader";
import { useDispatch } from "react-redux";
import {modalToggle, setToggleTab} from "../../../redux/projectsTrackerSlice";
import { apiRequest } from "../../../api/request";
import avatarMock1 from "../../../images/avatarMoсk1.png";
import avatarMock2 from "../../../images/avatarMoсk2.png";
import project from "../../../images/trackerProject.svg";
import watch from "../../../images/watch.png";
import file from "../../../images/fileModal.svg";
@ -28,36 +32,61 @@ import edit from "../../../images/edit.svg";
import "./ticketFullScreen.scss";
export const TicketFullScreen = ({}) => {
const [toggleTab, setToggleTab] = useState(1);
const [addSubtask, setAddSubtask] = useState(false);
const [modalAddWorker, setModalAddWorker] = useState(false);
const [valueTiket, setValueTiket] = useState("");
const ticketId = useParams();
const dispatch = useDispatch();
const navigate = useNavigate();
const [projectInfo, setProjectInfo] = useState({});
const [taskInfo, setTaskInfo] = useState({});
const [editOpen, setEditOpen] = useState(false);
const [inputsValue, setInputsValue] = useState({})
const [loader, setLoader] = useState(true)
const [tiket] = useState({
name: "Разработка трекера",
code: "PR - 2245",
creator: "Василий Тарасов",
descriptions:
"На многих страницах сайта отсутствуют или некорректно заполнены метатеги Description. Это может негативно повлиять на представление сайта в результатах поиска. Необходимо исправить все страницы где есть ошибки или отсутствует Title и Description.",
});
const [workers] = useState([
{
name: "Дмитрий Рогов",
avatar: avatarMock2,
},
{
name: "Марина Серова",
avatar: avatarMock1,
},
]);
useEffect(() => {
apiRequest(`/task/get-task?task_id=${ticketId.id}`).then((taskInfo) => {
setTaskInfo(taskInfo);
setInputsValue({title: taskInfo.title, description: taskInfo.description})
apiRequest(`/project/get-project?project_id=${taskInfo.project_id}`).then(
(project) => {
setProjectInfo(project);
setLoader(false)
}
);
});
}, []);
function deleteTask() {
apiRequest("/task/update-task", {
method: "PUT",
data: {
task_id: ticketId.id,
status: 0,
},
}).then((res) => {
navigate(`/tracker/project/${taskInfo.project_id}`);
});
}
function editTask() {
apiRequest("/task/update-task", {
method: "PUT",
data: {
task_id: taskInfo.id,
title: inputsValue.title,
description: inputsValue.description
},
}).then((res) => {
});
}
const toggleTabs = (index) => {
setToggleTab(index);
dispatch(setToggleTab(index));
};
return (
<section className="ticket-full-screen">
<ProfileHeader />
<Navigation />
<div className="container">
<div className="tracker__content">
<ProfileBreadcrumbs
@ -71,62 +100,54 @@ export const TicketFullScreen = ({}) => {
</div>
<div className="tracker__tabs">
<div className="tracker__tabs__head">
<div
className={toggleTab === 1 ? "tab active-tab" : "tab"}
<Link
to="/profile/tracker"
className="tab active-tab"
onClick={() => toggleTabs(1)}
>
<img src={project} alt="img" />
<p>Проекты </p>
</div>
<div
className={toggleTab === 2 ? "tab active-tab" : "tab"}
</Link>
<Link
to="/profile/tracker"
className="tab"
onClick={() => toggleTabs(2)}
>
<img src={tasks} alt="img" />
<Link to={`/profile/tracker`} className="link">
<p>Все мои задачи</p>
</Link>
</div>
<div
className={toggleTab === 3 ? "tab active-tab" : "tab"}
<p>Все мои задачи</p>
</Link>
<Link
to="/profile/tracker"
className="tab"
onClick={() => toggleTabs(3)}
>
<img src={archive} alt="img" />
<Link to={`/profile/tracker`} className="link">
<p>Архив</p>
</Link>
</div>
<p>Архив</p>
</Link>
</div>
{loader ? <Loader /> :
<>
<div className="tracker__tabs__content content-tabs">
<div className="tasks__head">
<div className="tasks__head__wrapper">
<h4>Проект : Разработка трекера</h4>
<h4>Проект : {projectInfo.name}</h4>
<ModalAdd active={modalAddWorker} setActive={setModalAddWorker}>
<div className="title-project">
<h4>Добавьте участника</h4>
<p className="title-project__decs">Введите имя или e-mail</p>
<div className="input-container">
<input
className="name-project"
value={valueTiket}
onChange={(e) => setValueTiket(e.target.value)}
></input>
</div>
</div>
<button
className="button-add"
onClick={(e) => e.preventDefault()}
>
Добавить
</button>
</ModalAdd>
<TrackerModal
active={modalAddWorker}
setActive={setModalAddWorker}
></TrackerModal>
<div className="tasks__head__persons">
<img src={avatarTest} alt="avatar" />
<img src={avatarTest} alt="avatar" />
<span className="countPersons">+9</span>
<span className="addPerson" onClick={setModalAddWorker}>
<span
className="addPerson"
onClick={() => {
dispatch(modalToggle("addWorker"));
setModalAddWorker(true);
}}
>
+
</span>
<p>добавить участника в проект</p>
@ -152,15 +173,23 @@ export const TicketFullScreen = ({}) => {
<div className="content ticket-whith">
<div className="content__task">
<span>Задача</span>
<h5>{tiket.code}</h5>
{editOpen ? <input value={inputsValue.title} onChange={(e) => {
setInputsValue((prevValue) => ({...prevValue, title: e.target.value}))
}} /> :<h5>{inputsValue.title}</h5>}
<div className="content__description">
<p>{tiket.descriptions}</p>
{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>
<p>{tiket.descriptions}</p>
</div>
<div className="content__communication">
<p className="tasks">
<button onClick={() => setAddSubtask(true)}>
<button
onClick={() => {
dispatch(modalToggle("addSubtask"));
setModalAddWorker(true);
}}
>
<img src={plus}></img>
Добавить под задачу
</button>
@ -182,20 +211,30 @@ export const TicketFullScreen = ({}) => {
</div>
<div className="workers">
<div className="workers_box">
<p className="workers__creator">Создатель : {tiket.creator}</p>
<p className="workers__creator">
Создатель : <span>{taskInfo.user?.fio}</span>
</p>
<div>
{workers.map((worker, index) => {
return (
<div className="worker" key={index}>
<img src={worker.avatar}></img>
<p>{worker.name}</p>
</div>
);
})}
{Boolean(taskInfo.taskUsers?.length) &&
taskInfo.taskUsers.map((worker, index) => {
return (
<div className="worker" key={index}>
<img src={worker.avatar}></img>
<p>{worker.name}</p>
</div>
);
})}
</div>
<div className="add-worker moreItems">
<button>+</button>
<button
onClick={() => {
dispatch(modalToggle("addWorker"));
setModalAddWorker(true);
}}
>
+
</button>
<span>Добавить участников</span>
</div>
</div>
@ -213,9 +252,16 @@ export const TicketFullScreen = ({}) => {
</div>
<div className="workers_box-bottom">
<div>
<div className={editOpen ? 'edit' : ''} onClick={() => {
if(editOpen) {
setEditOpen(!editOpen)
editTask()
} else {
setEditOpen(!editOpen)
}
}}>
<img src={edit}></img>
<p>редактировать</p>
<p>{editOpen ? 'сохранить' : 'редактировать'}</p>
</div>
<div>
<img src={link}></img>
@ -225,28 +271,15 @@ export const TicketFullScreen = ({}) => {
<img src={archive2}></img>
<p>в архив</p>
</div>
<div>
<div onClick={deleteTask}>
<img src={del}></img>
<p>удалить</p>
</div>
</div>
</div>
</div>
<ModalAdd active={addSubtask} setActive={setAddSubtask}>
<div className="title-project subtask">
<h4>
Вы добавляете подзадачу <p>в колонку задачи {"Готово"}</p>
</h4>
<p className="title-project__decs">Введите текст</p>
<div>
<textarea className="title-project__textarea"></textarea>
</div>
</div>
<button className="button-add" onClick={(e) => e.preventDefault()}>
Добавить
</button>
</ModalAdd>
</>
}
</div>
<Footer />
</section>

View File

@ -0,0 +1,314 @@
import React, {useEffect, useState} from "react";
import { useDispatch, useSelector } from "react-redux";
import { apiRequest } from "../../../api/request";
import { urlForLocal } from '../../../helper'
import {
setColumnName,
getProjectBoard,
getValueModalType,
setProject,
setProjectBoardFetch,
editProjectName,
editColumnName,
getColumnName,
getColumnId
} from "../../../redux/projectsTrackerSlice";
import arrowDown from "../../../images/selectArrow.png"
import "./trackerModal.scss";
export const TrackerModal = ({
active,
setActive,
selectedTab,
defautlInput,
titleProject,
projectId,
priorityTask
}) => {
const dispatch = useDispatch();
const projectBoard = useSelector(getProjectBoard);
const columnName = useSelector(getColumnName);
const columnId = useSelector(getColumnId)
const modalType = useSelector(getValueModalType);
const [projectName, setProjectName] = useState(defautlInput);
const [valueColumn, setValueColumn] = useState("");
const [nameProject, setNameProject] = useState("");
const [valueTiket, setValueTiket] = useState("");
const [descriptionTicket, setDescriptionTicket] = useState("");
const [workers, setWorkers] = useState([])
const [selectWorkersOpen, setSelectWorkersOpen] = useState(false)
const [selectedWorker, setSelectedWorker] = useState(null)
function createTab() {
if (!valueColumn) {
return;
}
apiRequest("/project-column/create-column", {
method: "POST",
data: {
project_id: projectBoard.id,
title: valueColumn,
},
}).then((res) => {
dispatch(setProjectBoardFetch(projectBoard.id));
});
setValueColumn("");
setActive(false);
}
function createTiket() {
if (!valueTiket || !descriptionTicket) {
return;
}
apiRequest("/task/create-task", {
method: "POST",
data: {
project_id: projectBoard.id,
title: valueTiket,
description: descriptionTicket,
status: 1,
user_id: localStorage.getItem("id"),
column_id: selectedTab,
priority: priorityTask
},
}).then((res) => {
dispatch(setProjectBoardFetch(projectBoard.id));
});
setActive(false);
setValueTiket("");
setDescriptionTicket("");
}
function editProject() {
apiRequest("/project/update", {
method: "PUT",
data: {
project_id: projectId,
name: projectName,
},
}).then((res) => {
setActive(false);
dispatch(editProjectName({ id: projectId, name: projectName }));
});
}
function changeColumnName() {
apiRequest("/project-column/update-column", {
method: "PUT",
data: {
column_id: columnId,
title: columnName
}
}).then((res) => {
setActive(false);
dispatch(editColumnName({id: columnId, title: columnName}))
})
}
function createProject() {
if (nameProject === "") {
return;
} else {
apiRequest("/project/create", {
method: "POST",
data: {
user_id: localStorage.getItem("id"),
name: nameProject,
status: 19,
},
}).then((res) => {
const result = { ...res, columns: [] };
dispatch(setProject(result));
setActive(false);
setNameProject("");
});
}
}
function addUserToProject() {
apiRequest("/project/add-user", {
method: "POST",
data: {
user_id: selectedWorker.id,
project_id: projectBoard.id
}
}).then((el) => {
setActive(false);
selectedWorker(null)
})
}
useEffect(() => {
modalType === "addWorker" ? apiRequest('/project/my-employee').then((el) => setWorkers(el.managerEmployees)) : ''
}, [modalType])
return (
<div
className={active ? "modal-add active" : "modal-add"}
onClick={() => setActive(false)}
>
<div className="modal-add__content" onClick={(e) => e.stopPropagation()}>
{modalType === "addWorker" && (
<div>
<div className="title-project">
<h4>Добавьте участника</h4>
{/*<div className="input-container">*/}
{/* <input*/}
{/* className="name-project"*/}
{/* value={emailWorker}*/}
{/* onChange={(e) => setEmailWorker(e.target.value)}*/}
{/* />*/}
{/*</div>*/}
<div className={selectWorkersOpen ? 'select__worker open' : 'select__worker'} onClick={() => setSelectWorkersOpen(!selectWorkersOpen)}>
<p>{selectedWorker ? selectedWorker.employee.fio : 'Выберите пользователя'}</p>
<img className='arrow' src={arrowDown} alt='arrow' />
{Boolean(selectWorkersOpen) &&
<div className='select__worker__dropDown'>
{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>
<button
className="button-add"
onClick={addUserToProject}
>
Добавить
</button>
</div>
)}
{modalType === "createTiketProject" && (
<div>
<div className="title-project">
<h4>Введите название и описание задачи</h4>
<div className="input-container">
<input
className="name-project"
value={valueTiket}
onChange={(e) => setValueTiket(e.target.value)}
placeholder="Название задачи"
/>
</div>
<div className="input-container">
<input
className="name-project"
value={descriptionTicket}
onChange={(e) => setDescriptionTicket(e.target.value)}
placeholder="Описание задачи"
/>
</div>
</div>
<button className="button-add" onClick={createTiket}>
Создать
</button>
</div>
)}
{modalType === "editProject" && (
<div>
<div className="title-project">
<h4>Введите новое название</h4>
<div className="input-container">
<input
className="name-project"
value={projectName}
onChange={(e) => setProjectName(e.target.value)}
/>
</div>
</div>
<button className="button-add" onClick={editProject}>
Сохранить
</button>
</div>
)}
{modalType === "createProject" && (
<div>
<div className="title-project">
<h4>{titleProject}</h4>
<div className="input-container">
<input
className="name-project"
value={nameProject}
onChange={(e) => setNameProject(e.target.value)}
/>
</div>
<button className="button-add" onClick={createProject}>
Создать
</button>
</div>
</div>
)}
{modalType === "addSubtask" && (
<div>
<div className="title-project subtask">
<h4>
Вы добавляете подзадачу{" "}
<p>в колонку(id) задачи "{defautlInput}"</p>
</h4>
<p className="title-project__decs">Введите текст</p>
<div>
<textarea className="title-project__textarea"></textarea>
</div>
</div>
<button className="button-add" onClick={(e) => e.preventDefault()}>
Добавить
</button>
</div>
)}
{modalType === "createColumn" && (
<div>
<div className="title-project">
<h4>Введите название колонки</h4>
<div className="input-container">
<input
className="name-project"
value={valueColumn}
onChange={(e) => setValueColumn(e.target.value)}
/>
</div>
</div>
<button className="button-add" onClick={createTab}>
Создать
</button>
</div>
)}
{modalType === "editColumn" && (
<div>
<div className="title-project">
<h4>Введите новое название</h4>
<div className="input-container">
<input
className="name-project"
value={columnName}
onChange={(e) => dispatch(setColumnName(e.target.value))}
/>
</div>
</div>
<button className="button-add" onClick={changeColumnName}>
Сохранить
</button>
</div>
)}
<span className="exit" onClick={() => setActive(false)}></span>
</div>
</div>
);
};
export default TrackerModal;

View File

@ -20,7 +20,7 @@
padding: 60px 60px 30px 60px;
display: flex;
flex-direction: column;
align-items: flex-start;
align-items: center;
justify-content: center;
}
@ -34,6 +34,7 @@
height: 35px;
background: #ffffff;
border-radius: 8px;
margin-top: 12px;
}
h4 {
@ -41,14 +42,13 @@
font-size: 22px;
line-height: 26px;
color: #263238 !important;
margin-bottom: 22px !important;
}
&__decs {
font-weight: 300;
font-size: 12px;
line-height: 14px;
margin: -13px 0 16px 0;
margin: 12px 0 16px 0;
}
&__textarea {
@ -61,6 +61,56 @@
font-size: 15px;
line-height: 18px;
}
.select__worker {
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px;
background: white;
border-radius: 5px;
cursor: pointer;
width: 100%;
position: relative;
p {
max-width: 150px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 14px;
}
img {
transition: all 0.3s ease;
width: 16px;
height: 16px;
}
&__dropDown {
display: flex;
flex-direction: column;
position: absolute;
width: 100%;
padding: 5px;
top: 35px;
left: 0;
background: white;
border-radius: 5px;
row-gap: 5px;
.worker {
display: flex;
justify-content: space-between;
}
}
}
.open {
.arrow {
transform: rotate(180deg);
}
}
}
.name-project {
@ -73,7 +123,7 @@
}
.button-add {
margin: 20px 40% 0 0;
margin: 20px;
width: 130px;
height: 37px;
background: #52b709;

View File

@ -1,4 +1,4 @@
import React, { Children } from "react"
import React from "react"
import "./bookkeepingTemplete.css"
import { Breadcrumps } from "../Breadcrumps/Breadcrumps"
import { Sidebar } from "../Sidebar/Sidebar"