Fixed conflicts

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

4
.prettierrc Normal file
View File

@ -0,0 +1,4 @@
{
"tabWidth": 2,
"useTabs": false
}

View File

@ -15,6 +15,7 @@ import ReportForm from "./components/ReportForm/ReportForm";
import FreeDevelopers from "./components/UI/FreeDevelopers/FreeDevelopers";
import { TicketFullScreen } from "./components/UI/TicketFullScreen/TicketFullScreen";
import { ProfileCalendar } from "./components/ProfileCalendar/ProfileCalendar";
import Article from "./pages/Article/Article";
import FormPage from "./pages/FormPage/FormPage.js";
import SingleReportPage from "./pages/SingleReportPage/SingleReportPage";
import { QuizPage } from "./pages/quiz/QuizPage";
@ -35,13 +36,14 @@ import { AuthForCandidate } from "./pages/AuthForCandidate/AuthForCandidate";
import { RegistrationForCandidate } from "./pages/RegistrationForCandidate/RegistrationForCandidate";
import { ProfileCandidate } from "./pages/ProfileCandidate/ProfileCandidate";
import { PassingTests } from "./pages/quiz/PassingTests";
import "./assets/global.scss";
import "./fonts/stylesheet.css";
import "bootstrap/dist/css/bootstrap.min.css";
import { FREQUENTLY_ASKED_QUESTIONS_ROUTE, FREQUENTLY_ASKED_QUESTION_ROUTE } from "./constants/router-path";
import Blog from "./pages/Blog/Blog";
import { ProjectTracker } from "./pages/ProjectTracker/ProjectTracker";
import { FrequentlyAskedQuestions } from "./pages/FrequentlyAskedQuestions/FrequentlyAskedQuestions";
import { FrequentlyAskedQuestion } from "./pages/FrequentlyAskedQuestion/FrequentlyAskedQuestion";
import "./assets/global.scss";
import "./fonts/stylesheet.css";
import "bootstrap/dist/css/bootstrap.min.css";
const App = () => {
return (
@ -50,15 +52,18 @@ const App = () => {
<Routes>
<Route exact path="/authdev" element={<AuthForDevelopers />} />
<Route exact path="/auth" element={<AuthForPartners />} />
<Route exact path={FREQUENTLY_ASKED_QUESTIONS_ROUTE} element={<FrequentlyAskedQuestions />}/>
<Route exact path={FREQUENTLY_ASKED_QUESTION_ROUTE+'/:id'} element={<FrequentlyAskedQuestion />} />
<Route exact path="/worker/:id" element={<FreeDevelopers />} />
<Route
exact
path="/tracker/:id"
path="/tracker/task/:id"
element={<TicketFullScreen />}
></Route>
<Route
exact
path="/tracker/project/:id"
element={<ProjectTracker />}
/>
<Route exact path="/auth-candidate" element={<AuthForCandidate />} />
<Route
exact
@ -66,6 +71,19 @@ const App = () => {
element={<RegistrationForCandidate />}
/>
<Route exact path="/blog" element={<Blog />}></Route>
<Route exact path="/blog/article/:id" element={<Article />}></Route>
<Route
exact
path="/frequently-asked-questions"
element={<FrequentlyAskedQuestions />}
/>
<Route
exact
path="/frequently-asked-question/:id"
element={<FrequentlyAskedQuestion />}
/>
<Route exact path="/candidate/:id" element={<Candidate />} />
<Route exact path="/candidate/:id/form" element={<FormPage />} />
<Route path="/:userId/calendar" element={<Calendar />} />
@ -83,8 +101,9 @@ const App = () => {
<Route index element={<Profile />} />
<Route exact path="catalog" element={<Home />} />
<Route exact path="calendar" element={<ProfileCalendar />} />
<Route exact path="calendar/view/" element={<ProfileCalendar />} />
<Route exact path="summary" element={<Summary />} />
<Route exact path="view" element={<ViewReport />} />
<Route exact path="view/:id" element={<ViewReport />} />
<Route exact path="tracker" element={<Tracker />} />
<Route exact path="payouts" element={<Payouts />} />
<Route exact path="settings" element={<PartnerSettings />} />
@ -104,7 +123,7 @@ const App = () => {
<Route index element={<ProfileCandidate />} />
</Route>
{/* <Route path="*" element={<Navigate to="/auth" replace />} /> */}
<Route path="*" element={<Navigate to="/auth" replace />} />
</Routes>
</Router>
</>

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"

View File

@ -1,2 +1,2 @@
export const FREQUENTLY_ASKED_QUESTIONS_ROUTE = '/frequently-asked-questions'
export const FREQUENTLY_ASKED_QUESTION_ROUTE = '/frequently-asked-question'
export const FREQUENTLY_ASKED_QUESTIONS_ROUTE = "/frequently-asked-questions";
export const FREQUENTLY_ASKED_QUESTION_ROUTE = "/frequently-asked-question";

10
src/images/blogArrow.svg Normal file
View File

@ -0,0 +1,10 @@
<svg width="27" height="27" viewBox="0 0 27 27" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle opacity="0.3" cx="13.5" cy="13.5" r="13.5" transform="matrix(-1 0 0 1 27 0)" fill="#807777"/>
<rect x="18.5625" y="16.3125" width="10.0195" height="5.625" transform="rotate(180 18.5625 16.3125)" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_1082_1300" transform="matrix(0.0244088 0 0 0.0434783 -0.000381388 0)"/>
</pattern>
<image id="image0_1082_1300" width="41" height="23" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAAXCAMAAAChzpYZAAAAAXNSR0IB2cksfwAAAEJQTFRFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjuH28gAAABZ0Uk5TAEK0Cvb/vgaIlxC7oNwcwg/J2f1bS4SvOREAAABWSURBVHicY2CAA0YmZgaiACMLKxs7kQpZOUYV4lPIyUUYgBUSBRiYiFTIysDMBiS5eQgDBrBSXj5ifESSUn6ilQoIDi2lQkTlDqBSYRGiFAKViuKVBgCepwfqGDtvmgAAAABJRU5ErkJggg=="/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 KiB

13
src/images/yandexZen.svg Normal file
View File

@ -0,0 +1,13 @@
<svg width="39" height="39" viewBox="0 0 39 39" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1082_1494)">
<path d="M0 19.021V17.1032C0.3039 16.8841 0.206332 16.5274 0.267112 16.2395C2.02653 7.82463 7.07288 2.56716 15.3933 0.428659C15.91 0.294303 16.5002 0.404667 16.9432 0C17.6358 0 18.3284 0 19.021 0C19.0513 0.0383874 19.1073 0.0783743 19.1057 0.116762C19.0386 3.38449 19.0354 6.66021 18.354 9.87836C17.3959 14.3953 14.736 17.2599 10.1599 18.274C7.99578 18.7538 5.80449 18.9602 3.59882 19.0114C2.40081 19.0402 1.19961 19.2113 0 19.021Z" fill="#040505"/>
<path d="M0 20.2989C3.3541 20.2797 6.6986 20.3724 9.99992 21.0666C14.6448 22.0423 17.3847 24.8989 18.3524 29.4942C19.0034 32.5812 19.0338 35.7242 19.0977 38.8624C19.0977 38.9071 19.0481 38.9535 19.021 38.9999H16.9432C16.7721 38.6816 16.433 38.7632 16.1755 38.7184C8.40044 37.3812 2.16569 31.4424 0.454251 23.7649C0.326293 23.1939 0.415863 22.5541 0 22.0567V20.2989Z" fill="#040505"/>
<path d="M38.9999 19.021C37.7284 19.1729 36.4552 19.0434 35.1868 18.9938C33.0355 18.9106 30.8874 18.7187 28.7921 18.1828C24.2416 17.0184 21.764 13.973 20.9419 9.4545C20.3741 6.32752 20.2637 3.16856 20.2989 0H21.7368C21.772 0.180741 21.9192 0.147152 22.0439 0.164746C30.0685 1.26678 36.3912 6.91773 38.4289 14.7664C38.6305 15.5437 38.6033 16.3754 38.9999 17.1016V19.0194V19.021Z" fill="#040505"/>
<path d="M20.2992 39.0001C20.2912 35.7788 20.3984 32.5686 21.0622 29.3985C22.081 24.5329 25.1296 21.8154 29.9425 20.942C32.9383 20.3982 35.9645 20.2687 39.0003 20.2991V22.0569C38.6852 22.2296 38.7652 22.5671 38.7204 22.8246C37.3384 30.6685 31.4316 36.8456 23.6933 38.5555C23.0423 38.6994 22.3418 38.645 21.7372 38.9985H20.2992V39.0001Z" fill="#040505"/>
</g>
<defs>
<clipPath id="clip0_1082_1494">
<rect width="39" height="39" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,138 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
import AuthHeader from "../../components/AuthHeader/AuthHeader";
import SideBar from "../../components/SideBar/SideBar";
import { Footer } from "../../components/Footer/Footer";
import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs";
import CardArticle from "../../components/UI/CardArticle/CardArticle";
import mockImgArticle from "../../images/mockImgArticle.png";
import rightArrow from "../../images/right-arrow.png";
import yandexZen from "../../images/yandexZen.svg";
import cardCalendar from "../../images/cardCalendar.svg";
import cardImg1 from "../../images/cardArticleItem.png";
import cardImg2 from "../../images/cardArticleItem2.png";
import cardImg3 from "../../images/cardArticleItem3.png";
import "./article.scss";
export const Article = ({}) => {
const [article] = useState([
{
image: cardImg1,
title: "Аутстаффинг джунов: почему это выгодно",
data: "1 марта, 2023",
},
{
image: cardImg2,
title: "Аутстаффинг джунов: почему это выгодно",
data: "1 марта, 2023",
},
{
image: cardImg3,
title: "Аутстаффинг джунов: почему это выгодно",
data: "1 марта, 2023",
},
]);
return (
<div className="article-blog">
<AuthHeader />
<SideBar />
<div className="container">
<div className="article-blog__breadCrumbs">
<ProfileBreadcrumbs
links={[
{ name: "Главная", link: "/auth" },
{ name: "Блог", link: "/blog" },
{
name: "Аутстаффинг джунов: почему это выгодно",
link: "/blog",
},
]}
/>
</div>
<div className="article-blog__title">
<h1>Аутстаффинг джунов: почему это выгодно</h1>
</div>
<div className="article-blog__return">
<Link to={"/blog"} className="article-blog__return-link">
<div className="article-blog__return-arrow">
<img src={rightArrow} />
</div>
<span>вернуться к списку статей</span>
</Link>
</div>
<div className="article-blog__body">
<div className="article-blog__body-text">
<p>
Нет, мы работаем только с юридическими лицами и индивидуальными
предпринимателями и тщательно проверяем своих партнеров.
Партнерами являются агентства, которые специализируются на
оказании услуг в формате аутстафф-модели и обладают глубокой
экспертизой в разработке и внедрении ИТ-проектов.
</p>
</div>
<img src={mockImgArticle} className="article-blog__body-img" />
<div className="article-blog__body-text">
<p>
С одной стороны, зарплаты в сфере разработки растут, с другой
стороны, появляется огромное количество новичков, которые хотят
легко и просто войти в ИТ-сферу на волне востребованности и
больших зарплат. Разумеется, это приводит к осторожному отношению
работодателя к выпускникам различных курсов. Нет такого курса,
который даст на 100% готового джуна, слишком многое завязано на
личной инициативе, обучаемости и желании.
</p>
<br />
<p>
В итоге получается, что взгляды работодателя и потенциального
сотрудника расходятся: работодатель не хочет открывать ящик
пандоры, на который нужно тратить время, а работник, только
прошедший курсы, испытывает эффект завышенных ожиданий и имеет
зачастую неадекватные запросы.
</p>
</div>
<div className="article-blog__body-footer">
<div className="yandex">
<img src={yandexZen} />
<div className="yandex__text">
<p>Читать на Дзен</p>
<span>dzen.ru</span>
</div>
</div>
<div className="publication-date">
<img src={cardCalendar} />
<p>1 марта, 2023</p>
</div>
</div>
<div className="more-articles">
<h1>Читайте также</h1>
<div className="more-articles__arrow">
<img src={rightArrow} />
</div>
</div>
<div className="blog__body">
{article.map((item, index) => {
return (
<CardArticle
images={item.image}
title={item.title}
data={item.data}
key={index}
id={index}
/>
);
})}
</div>
</div>
</div>
<Footer />
</div>
);
};
export default Article;

View File

@ -0,0 +1,166 @@
.article-blog {
background: #f1f1f1;
&__breadCrumbs {
margin-top: 30px;
@media (max-width: 1375px) {
margin-top: 0;
padding-top: 110px;
}
@media (max-width: 600px) {
display: none;
}
}
&__title {
margin: 42px 0 44px 0;
h1 {
font-weight: 500;
font-size: 38px;
line-height: 32px;
}
@media (max-width: 600px) {
padding-top: 80px;
}
}
&__return {
display: flex;
align-items: center;
&-link {
display: flex;
align-items: center;
}
span {
font-weight: 400;
font-size: 12px;
line-height: 20px;
color: #6f6f6f;
margin-left: 19px;
}
&-arrow {
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
border-radius: 44px;
background: #8dc63f78;
img {
width: 45%;
}
}
}
&__body {
display: flex;
flex-direction: column;
&-text {
margin-top: 30px;
padding: 33px;
background: #ffffff;
text-align: justify;
border-radius: 12px;
p {
font-style: normal;
font-weight: 400;
font-size: 20px;
line-height: 24px;
}
}
&-img {
margin-top: 16px;
}
&-footer {
margin: 30px 0 78px 25px;
display: flex;
.yandex {
display: flex;
align-items: center;
img {
margin: 0;
width: 39px;
height: 39px;
}
&__text {
margin-left: 18px;
p {
font-weight: 500;
font-size: 16px;
line-height: 32px;
}
}
}
.publication-date {
margin-left: 76px;
display: flex;
align-items: center;
p {
font-weight: 400;
font-size: 14px;
line-height: 20px;
}
img {
margin: 0 16px 0 0;
}
}
@media (max-width: 600px) {
margin: 20px 0 20px 0;
flex-direction: column;
.publication-date {
margin: 0;
}
.yandex {
margin: 0 0 10px 0;
}
}
}
.more-articles {
display: flex;
align-items: center;
margin-bottom: 49px;
h1 {
font-weight: 500;
font-size: 33px;
line-height: 32px;
}
&__arrow {
margin: 0 0 0 21px;
display: flex;
justify-content: center;
align-items: center;
width: 27px;
height: 27px;
background: #80777770;
border-radius: 44px;
img {
width: 50%;
transform: rotate(180deg);
}
}
}
}
}

106
src/pages/Blog/Blog.jsx Normal file
View File

@ -0,0 +1,106 @@
import React, { useState } from "react";
import AuthHeader from "../../components/AuthHeader/AuthHeader";
import SideBar from "../../components/SideBar/SideBar";
import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs";
import { Footer } from "../../components/Footer/Footer";
import CardArticle from "../../components/UI/CardArticle/CardArticle";
// import arrowRight from "../../images/arrowRight.png";
import blogArrow from "../../images/blogArrow.svg";
import cardImg1 from "../../images/cardArticleItem.png";
import cardImg2 from "../../images/cardArticleItem2.png";
import cardImg3 from "../../images/cardArticleItem3.png";
import cardImg4 from "../../images/cardArticleItem4.png";
import cardImg5 from "../../images/cardArticleItem5.png";
import cardImg6 from "../../images/cardArticleItem6.png";
import "./blog.scss";
export const Blog = ({}) => {
const [article] = useState([
{
image: cardImg1,
title: "Аутстаффинг джунов: почему это выгодно",
data: "1 марта, 2023",
},
{
image: cardImg2,
title: "Аутстаффинг джунов: почему это выгодно",
data: "1 марта, 2023",
},
{
image: cardImg3,
title: "Аутстаффинг джунов: почему это выгодно",
data: "1 марта, 2023",
},
{
image: cardImg4,
title: "Аутстаффинг джунов: почему это выгодно",
data: "1 марта, 2023",
},
{
image: cardImg5,
title: "Аутстаффинг джунов: почему это выгодно",
data: "1 марта, 2023",
},
{
image: cardImg6,
title: "Аутстаффинг джунов: почему это выгодно",
data: "1 марта, 2023",
},
]);
return (
<div className="blog">
<AuthHeader />
<SideBar />
<div className="container">
<div className="blog__breadCrumbs">
<ProfileBreadcrumbs
links={[
{ name: "Главная", link: "/auth" },
{ name: "Блог", link: "/blog" },
]}
/>
</div>
<div className="blog__title">
<div>
<h1>Блог</h1>
<img src={blogArrow} className="blog__title-arrow" />
</div>
<h3>
Из первых уст рассказываем о себе пользователям, делимся полезными и
важными материалами, стремимся получать обратную связь
</h3>
</div>
<div className="blog__body">
{article.map((item, index) => {
return (
<CardArticle
images={item.image}
title={item.title}
data={item.data}
key={index}
id={index}
/>
);
})}
</div>
<div className="blog__load-more">
<button>Загрузить еще</button>
</div>
</div>
<Footer />
</div>
);
};
export default Blog;

77
src/pages/Blog/blog.scss Normal file
View File

@ -0,0 +1,77 @@
.blog {
background: #f1f1f1;
&__breadCrumbs {
margin-top: 30px;
@media (max-width: 1375px) {
margin-top: 0;
padding-top: 100px;
}
@media (max-width: 600px) {
display: none;
}
}
&__title {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 40px;
h1 {
font-weight: 500;
font-size: 48px;
line-height: 32px;
}
h3 {
font-weight: 400;
font-size: 18px;
line-height: 28px;
text-align: justify;
}
div {
display: flex;
}
&-arrow {
margin: 0 27px 0 20px;
}
@media (max-width: 600px) {
padding-top: 110px;
flex-direction: column;
div {
margin: 0 0 15px 0;
}
}
}
&__body {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
}
&__load-more {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 35px;
button {
background: #52b709;
border-radius: 44px;
width: 202px;
height: 50px;
color: white;
font-size: 16px;
line-height: 32px;
border: none;
}
}
}

View File

@ -1,21 +0,0 @@
import { useEffect, useState } from "react";
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { a11yDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
export const CodeSnippetlighter = () => {
const [codeString, setCodeString] = useState(``)
//
// useEffect(()=>{
// fetch('/code.txt')
// .then((r) => r.text())
// .then(text => {
// setCodeString(text)
// })
// }, [])
return (
<SyntaxHighlighter language={"javascript"} style={a11yDark} wrapLongLines={false} customStyle={{fontSize:14}} showLineNumbers={true}>
{codeString}
</SyntaxHighlighter>
);
};

View File

@ -1,13 +0,0 @@
import React from 'react';
import { BookkeepingTemplete } from "../components/features/bookkeeping/BookkeepingTemplete/BookkeepingTemplete"
import { ContractContent } from "../components/features/bookkeeping/ContractContent/ContractContent"
export const ContractPage = () => {
return (
<div>
<BookkeepingTemplete showBreadcrumps nameBreeadcrumps="Создание договора">
<ContractContent />
</BookkeepingTemplete>
</div>
)
}

View File

@ -1,13 +0,0 @@
import React from 'react';
import { BookkeepingTemplete } from "../components/features/bookkeeping/BookkeepingTemplete/BookkeepingTemplete"
import { MoneyContent } from "../components/features/Money/MoneyContent"
export const MoneyPage = () => {
return (
<div>
<BookkeepingTemplete>
<MoneyContent></MoneyContent>
</BookkeepingTemplete>
</div>
)
}

View File

@ -13,7 +13,7 @@ import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileB
import { Footer } from "../../components/Footer/Footer";
import { Navigation } from "../../components/Navigation/Navigation";
import { Loader } from "../../components/Loader/Loader";
import ModalAdd from "../../components/UI/ModalAdd/ModalAdd";
import ModalLayout from "../../components/UI/ModalLayout/ModalLayout";
import { apiRequest } from "../../api/request";
import { getCorrectDate } from "../../components/Calendar/calendarHelper";
@ -76,7 +76,7 @@ export const PartnerBid = () => {
<div className="partnerBid">
<ProfileHeader />
<Navigation />
<ModalAdd active={modalDelete} setActive={setModalDelete}>
<ModalLayout active={modalDelete} setActive={setModalDelete}>
<div className="title-project modal-title-delete">
<h4>Подтверждение удаления</h4>
<p className="title-project__decs modal-decs">
@ -99,7 +99,7 @@ export const PartnerBid = () => {
</button>
</div>
</div>
</ModalAdd>
</ModalLayout>
<div className="container">
<ProfileBreadcrumbs

View File

@ -0,0 +1,395 @@
import React, { useEffect, useRef, useState } from "react";
import { Link, useParams } from "react-router-dom";
import { ProfileHeader } from "../../components/ProfileHeader/ProfileHeader";
import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs";
import { Footer } from "../../components/Footer/Footer";
import { Navigation } from "../../components/Navigation/Navigation";
import { Loader } from "../../components/Loader/Loader";
import { useDispatch, useSelector } from "react-redux";
import { apiRequest } from "../../api/request";
import {
getProjectBoard,
getBoarderLoader,
modalToggle,
moveProjectTask,
setProjectBoardFetch,
setToggleTab,
activeLoader,
setColumnName,
setColumnId,
} from "../../redux/projectsTrackerSlice";
import ModalTicket from "../../components/UI/ModalTicket/ModalTicket";
import TrackerModal from "../../components/UI/TrackerModal/TrackerModal";
import project from "../../images/trackerProject.svg";
import tasks from "../../images/trackerTasks.svg";
import archive from "../../images/archiveTracker.svg";
import selectArrow from "../../images/select.svg";
import commentsBoard from "../../images/commentsBoard.svg";
import filesBoard from "../../images/filesBoard.svg";
import arrow from "../../images/arrowCalendar.png";
import del from "../../images/delete.svg";
import edit from "../../images/edit.svg";
export const ProjectTracker = () => {
const dispatch = useDispatch();
const projectId = useParams();
const [openColumnSelect, setOpenColumnSelect] = useState({});
const [selectedTab, setSelectedTab] = useState(0);
const [priorityTask, setPriorityTask] = useState(0);
const [wrapperHover, setWrapperHover] = useState({});
const [modalAdd, setModalAdd] = useState(false);
const [modalActiveTicket, setModalActiveTicket] = useState(false);
const [selectedTicket, setSelectedTicket] = useState({});
const startWrapperIndexTest = useRef({});
const projectBoard = useSelector(getProjectBoard);
const loader = useSelector(getBoarderLoader);
useEffect(() => {
dispatch(activeLoader());
dispatch(setProjectBoardFetch(projectId.id));
}, []);
useEffect(() => {
if (Object.keys(projectBoard).length) {
projectBoard.columns.forEach((column) => {
setOpenColumnSelect((prevState) => ({
...prevState,
[column.id]: false,
}));
setWrapperHover((prevState) => ({ ...prevState, [column.id]: false }));
});
}
}, [projectBoard]);
// function toggleMoreTasks(columnId) {
// setTabTaskMok((prevArray) =>
// prevArray.map((elem, index) => {
// if (columnId === index) {
// return { ...elem, open: !elem.open };
// } else {
// return elem;
// }
// })
// );
// }
function dragStartHandler(e, task, columnId) {
startWrapperIndexTest.current = { task: task, index: columnId };
setTimeout(() => {
e.target.classList.add("tasks__board__item__hide");
}, 0);
}
function dragEndHandler(e) {
setWrapperHover((prevState) => ({
[prevState]: false,
}));
e.target.classList.remove("tasks__board__item__hide");
}
function dragOverHandler(e) {
e.preventDefault();
}
function dragEnterHandler(columnId) {
if (columnId === startWrapperIndexTest.current.index) {
return;
}
setWrapperHover((prevState) => ({
[prevState]: false,
[columnId]: true,
}));
}
function dragDropHandler(e, columnId) {
e.preventDefault();
if (startWrapperIndexTest.current.index === columnId) {
return;
}
setWrapperHover((prevState) => ({
[prevState]: false,
}));
if (columnId !== startWrapperIndexTest.current.index) {
dispatch(
moveProjectTask({
startWrapperIndex: startWrapperIndexTest.current,
columnId,
})
);
}
}
function selectedTabTask(columnId, length) {
setSelectedTab(columnId);
dispatch(modalToggle("createTiketProject"));
setModalAdd(true);
setPriorityTask(length + 1)
}
function openTicket(e, task) {
setSelectedTicket(task);
setModalActiveTicket(true);
}
function deleteColumn(id) {
apiRequest("/project-column/update-column", {
method: "PUT",
data: {
column_id: id,
project_id: projectBoard.id,
status: 0,
},
}).then((res) => {
dispatch(setProjectBoardFetch(projectBoard.id));
});
}
return (
<div className="tracker">
<ProfileHeader />
<Navigation />
<div className="container">
<div className="tracker__content">
<ProfileBreadcrumbs
links={[
{ name: "Главная", link: "/profile" },
{ name: "Трекер", link: "/profile/tracker" },
]}
/>
<h2 className="tracker__title">Управление проектами с трекером</h2>
</div>
</div>
<div className="tracker__tabs">
<div className="tracker__tabs__head">
<Link
to="/profile/tracker"
className="tab active-tab tab"
onClick={() => dispatch(setToggleTab(1))}
>
<img src={project} alt="img" />
<p>Проекты </p>
</Link>
<Link
to="/profile/tracker"
className="tab"
onClick={() => dispatch(setToggleTab(2))}
>
<img src={tasks} alt="img" />
<p>Все мои задачи</p>
</Link>
<Link
to="/profile/tracker"
className="tab"
onClick={() => dispatch(setToggleTab(3))}
>
<img src={archive} alt="img" />
<p>Архив</p>
</Link>
</div>
<div className="tracker__tabs__content">
<TrackerModal
active={modalAdd}
setActive={setModalAdd}
selectedTab={selectedTab}
priorityTask={priorityTask}
/>
{loader && <Loader style="green" />}
{!loader && (
<div className="tracker__tabs__content__tasks tasks active__content">
<div className="tasks__head">
<div className="tasks__head__wrapper">
<h4>Проект : {projectBoard.name}</h4>
<div className="tasks__head__add">
<span
onClick={() => {
dispatch(modalToggle("createColumn"));
setModalAdd(true);
}}
>
+
</span>
<p>добавить колонку</p>
</div>
<div className="tasks__head__persons">
{/*<img src={avatarTest} alt="avatar" />*/}
{/*<img src={avatarTest} alt="avatar" />*/}
<span className="countPersons">{projectBoard.projectUsers?.length}</span>
<span
className="addPerson"
onClick={() => {
dispatch(modalToggle("addWorker"));
setModalAdd(true);
}}
>
+
</span>
<p>добавить участника</p>
</div>
<div className="tasks__head__select">
<span>Участвую</span>
<img src={selectArrow} alt="arrow" />
</div>
<div className="tasks__head__select">
<span>Мои</span>
<img src={selectArrow} alt="arrow" />
</div>
<Link to="/profile/tracker" className="tasks__head__back">
<p>Вернуться на проекты</p>
<img src={arrow} alt="arrow" />
</Link>
</div>
</div>
{Boolean(modalActiveTicket) && <ModalTicket
active={modalActiveTicket}
setActive={setModalActiveTicket}
task={selectedTicket}
projectId={projectBoard.id}
projectName={projectBoard.name}
/>}
<div className="tasks__container">
{Boolean(projectBoard?.columns) &&
Boolean(projectBoard.columns.length) &&
projectBoard.columns.map((column) => {
return (
<div
key={column.id}
onDragOver={(e) => dragOverHandler(e)}
onDragEnter={(e) => dragEnterHandler(column.id)}
onDrop={(e) => dragDropHandler(e, column.id)}
className={`tasks__board ${
column.tasks.length >= 3 ? "tasks__board__more" : ""
} ${
wrapperHover[column.id] ? "tasks__board__hover" : ""
}`}
>
<div className="board__head">
{/*<span className={wrapperIndex === 3 ? "done" : ""}>*/}
<span>{column.title}</span>
<div>
<span
className="add"
onClick={() => selectedTabTask(column.id, column.tasks.length)}
>
+
</span>
<span
onClick={() => {
setOpenColumnSelect((prevState) => ({
...prevState,
[column.id]: true,
}));
}}
className="more"
>
...
</span>
</div>
</div>
{openColumnSelect[column.id] && (
<div className="column__select">
<div
className="column__select__item"
onClick={() => {
setOpenColumnSelect((prevState) => ({
...prevState,
[column.id]: false,
}));
dispatch(modalToggle("editColumn"));
dispatch(setColumnName(column.title))
dispatch(setColumnId(column.id))
setModalAdd(true);
}}
>
<img src={edit} alt="edit" />
<span>Изменить</span>
</div>
<div
className="column__select__item"
onClick={() => deleteColumn(column.id)}
>
<img src={del} alt="delete" />
<span>Удалить</span>
</div>
</div>
)}
{column.tasks.map((task, index) => {
// if (index > 2) {
// if (!column.open) {
// return;
// }
// }
return (
<div
key={task.id}
className="tasks__board__item"
draggable={true}
onDragStart={(e) =>
dragStartHandler(e, task, column.id)
}
onDragEnd={(e) => dragEndHandler(e)}
onClick={(e) => openTicket(e, task)}
>
<div className="tasks__board__item__title">
<p>{task.title}</p>
</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.comment_count} коментариев</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>
);
})}
{/*{column.tasks.length > 3 && (*/}
{/* <span*/}
{/* className={*/}
{/* column.open*/}
{/* ? "lessItems openItems"*/}
{/* : "moreItems openItems"*/}
{/* }*/}
{/* // onClick={() => toggleMoreTasks(column.id)}*/}
{/* >*/}
{/* {column.open ? "-" : "+"}*/}
{/* </span>*/}
{/*)}*/}
</div>
);
})}
{Boolean(projectBoard?.columns) &&
!Boolean(projectBoard.columns.length) && (
<div className="tasks__board__noItems">
В проекте нет задач.
</div>
)}
</div>
</div>
)}
</div>
</div>
<Footer />
</div>
);
};

View File

@ -1,11 +0,0 @@
import React from 'react';
import { BookkeepingTemplete } from "../components/features/bookkeeping/BookkeepingTemplete/BookkeepingTemplete"
import { TaxContent } from "../components/features/Taxes/TaxContent/TaxContent"
export const TaxPage = () => {
return (
<BookkeepingTemplete>
<TaxContent></TaxContent>
</BookkeepingTemplete>
)
}

File diff suppressed because it is too large Load Diff

View File

@ -53,6 +53,7 @@
padding: 12px 40px 15px 19px;
cursor: pointer;
align-items: center;
color: black;
@media (max-width: 490px) {
padding: 8px 20px 8px 14px;
@ -250,6 +251,8 @@
font-size: 14px;
line-height: 17px;
max-width: 120px;
display: flex;
align-items: center;
}
}
@ -286,7 +289,7 @@
color: #252c32;
border: 1px solid #dde2e4;
background: white;
left: -25px;
left: -18px;
z-index: 2;
}
@ -294,8 +297,8 @@
background: #00c5a8;
color: white;
font-size: 14px;
left: -35px;
z-index: 1;
left: -30px;
z-index: 2;
}
p {
@ -326,6 +329,7 @@
cursor: pointer;
display: flex;
align-items: center;
color: black;
p {
font-weight: 400;
@ -395,6 +399,9 @@
border-radius: 6px;
background: #ffffff;
cursor: pointer;
display: flex;
flex-direction: column;
justify-content: space-between;
&__hide {
opacity: 0;
@ -446,6 +453,8 @@
&__more {
cursor: pointer;
display: flex;
align-items: center;
span {
font-weight: 500;
font-size: 12px;
@ -494,6 +503,37 @@
&__more {
padding-bottom: 50px;
}
.column__select {
position: absolute;
padding: 15px;
background: #e1fccf;
border-radius: 12px;
right: -20px;
top: 5px;
z-index: 7;
row-gap: 10px;
display: flex;
flex-direction: column;
&__item {
cursor: pointer;
display: flex;
align-content: center;
img {
margin-right: 5px;
}
span {
font-size: 14px;
}
}
}
&__noItems {
font-weight: 500;
font-size: 25px;
}
}
.board {

View File

@ -1,175 +0,0 @@
import React, {useEffect, useState} from 'react'
import {Link, Navigate} from "react-router-dom";
import {useSelector} from "react-redux";
import {getReportDate} from "../../redux/reportSlice";
import {Loader} from "../../components/Loader/Loader"
import {ProfileHeader} from "../../components/ProfileHeader/ProfileHeader";
import {ProfileBreadcrumbs} from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs"
import {Footer} from "../../components/Footer/Footer";
import {apiRequest} from "../../api/request";
import {getCorrectDate, getCreatedDate, hourOfNum} from '../../components/Calendar/calendarHelper'
import arrow from "../../images/right-arrow.png";
import arrowSwitchDate from "../../images/arrowViewReport.png";
import './viewReport.scss'
import { Navigation } from '../../components/Navigation/Navigation';
export const ViewReport = () => {
if(localStorage.getItem('role_status') === '18') {
return <Navigate to="/profile" replace/>
}
const reportDate = useSelector(getReportDate);
const [taskText, setTaskText] = useState([]);
const [difficulties, setDifficulties] = useState([]);
const [tomorrowTask, setTomorrowTask] = useState([]);
const [totalHours, setTotalHours] = useState(0);
const [reportDay] = useState(new Date (getCreatedDate(reportDate)));
const [currentDay] = useState(new Date ());
const [loader, setLoader] = useState(false);
function getReportFromDate(day) {
setLoader(true);
setTaskText([]);
setDifficulties([]);
setTomorrowTask([]);
apiRequest(`reports/find-by-date?user_card_id=${localStorage.getItem('cardId')}&date=${day}`)
.then(res => {
let spendTime = 0;
for (const item of res) {
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)
})
}
function nextDay() {
reportDay.setDate(reportDay.getDate() + 1);
getReportFromDate(getCreatedDate(reportDay))
}
function previousDay() {
reportDay.setDate(reportDay.getDate() - 1);
getReportFromDate(getCreatedDate(reportDay))
}
useEffect(() => {
getReportFromDate(getCreatedDate(reportDate))
}, []);
return (
<div className='viewReport'>
<ProfileHeader/>
<Navigation />
<div className='container'>
<div className='viewReport__info'>
<ProfileBreadcrumbs links={[{name: 'Главная', link: '/profile'},
{name: 'Ваша отчетность', link: '/profile/calendar'},
{name: 'Просмотр отчета за день', link: '/profile/view'}]}
/>
<h2 className='viewReport__title'>Ваши отчеты - <span>просмотр отчета за день</span></h2>
<Link className='viewReport__back' to={`/profile/calendar`}>
<img src={arrow} alt='arrow'/><p>Вернуться</p>
</Link>
<div className='viewReport__bar'>
<h3 className='viewReport__bar__date'>{getCorrectDate(reportDay)}</h3>
<p className='viewReport__bar__hours'>Вами потрачено на работу : <span>{totalHours} {hourOfNum(totalHours)}</span></p>
{/*<div className='viewReport__bar__progressBar'>*/}
{/* <span></span>*/}
{/*</div>*/}
{/*<p className='viewReport__bar__total'>122 часа из 160</p>*/}
</div>
</div>
<div className='viewReport__switchDate'>
<div className='viewReport__switchDate__prev switchDate' onClick={() => previousDay()}>
<img src={arrowSwitchDate} alt='arrow'/>
</div>
<p>{getCorrectDate(reportDay)}</p>
<div className={`viewReport__switchDate__next switchDate ${getCreatedDate(currentDay) === getCreatedDate(reportDay) ? 'disable' : ''}`} onClick={() => nextDay()}>
<img src={arrowSwitchDate} alt='arrow'/>
</div>
</div>
{loader &&
<Loader width={75} height={75}/>
}
{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>
}
<Footer />
</div>
</div>
)
};

View File

@ -0,0 +1,225 @@
import React, { useEffect, useState } from "react";
import { Link, Navigate, useParams } from "react-router-dom";
import { Loader } from "../../components/Loader/Loader";
import { ProfileHeader } from "../../components/ProfileHeader/ProfileHeader";
import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs";
import { Footer } from "../../components/Footer/Footer";
import { Navigation } from "../../components/Navigation/Navigation";
import { apiRequest } from "../../api/request";
import {
getCorrectDate,
getCreatedDate,
hourOfNum,
} from "../../components/Calendar/calendarHelper";
import arrow from "../../images/right-arrow.png";
import arrowSwitchDate from "../../images/arrowViewReport.png";
import "./viewReport.scss";
export const ViewReport = () => {
if (localStorage.getItem("role_status") === "18") {
return <Navigate to="/profile" replace />;
}
const dateReport = useParams();
const [previousReportDay] = useState(new Date(dateReport.id));
const [nextReportDay] = useState(new Date(dateReport.id));
const [taskText, setTaskText] = useState([]);
const [difficulties, setDifficulties] = useState([]);
const [tomorrowTask, setTomorrowTask] = useState([]);
const [totalHours, setTotalHours] = useState(0);
const [currentDay] = useState(new Date());
const [loader, setLoader] = useState(false);
function getReportFromDate(day) {
setLoader(true);
setTaskText([]);
setDifficulties([]);
setTomorrowTask([]);
apiRequest(
`reports/find-by-date?user_card_id=${localStorage.getItem(
"cardId"
)}&date=${day}`
).then((res) => {
let spendTime = 0;
for (const item of res) {
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);
});
previousReportDay.setDate(previousReportDay.getDate() - 1);
nextReportDay.setDate(nextReportDay.getDate() + 1);
}
function nextDay() {
getReportFromDate(getCreatedDate(nextReportDay));
previousReportDay.setDate(previousReportDay.getDate() + 2);
}
function previousDay() {
getReportFromDate(getCreatedDate(previousReportDay));
nextReportDay.setDate(nextReportDay.getDate() - 2);
}
useEffect(() => {
getReportFromDate(dateReport.id);
}, []);
return (
<div className="viewReport">
<ProfileHeader />
<Navigation />
<div className="container">
<div className="viewReport__info">
<ProfileBreadcrumbs
links={[
{ name: "Главная", link: "/profile" },
{ name: "Ваша отчетность", link: "/profile/calendar" },
{ name: "Просмотр отчета за день", link: "/profile/view" },
]}
/>
<h2 className="viewReport__title">
Ваши отчеты - <span>просмотр отчета за день</span>
</h2>
<Link className="viewReport__back" to={`/profile/calendar`}>
<img src={arrow} alt="arrow" />
<p>Вернуться</p>
</Link>
<div className="viewReport__bar">
<h3 className="viewReport__bar__date">
{getCorrectDate(dateReport.id)}
</h3>
<p className="viewReport__bar__hours">
Вами потрачено на работу :{" "}
<span>
{totalHours} {hourOfNum(totalHours)}
</span>
</p>
</div>
</div>
<div className="viewReport__switchDate">
<div
onClick={() => {
previousDay();
}}
>
<Link to={`../view/${getCreatedDate(previousReportDay)}`}>
<div className="viewReport__switchDate__prev switchDate">
<img src={arrowSwitchDate} alt="arrow" />
</div>
</Link>
</div>
<p>{getCorrectDate(dateReport.id)}</p>
<div
onClick={() => nextDay()}
className={`${
getCreatedDate(currentDay) === dateReport.id ? "disable" : ""
}`}
>
<Link to={`../view/${getCreatedDate(nextReportDay)}`}>
<div className={`viewReport__switchDate__next switchDate`}>
<img src={arrowSwitchDate} alt="arrow" />
</div>
</Link>
</div>
</div>
{loader && <Loader width={75} height={75} />}
{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>
)}
<Footer />
</div>
</div>
);
};

View File

@ -1 +0,0 @@
Hellow

View File

@ -1,34 +1,114 @@
import { createSlice } from "@reduxjs/toolkit";
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { apiRequest } from "../api/request";
const initialState = {
project: [
{
name: "Разработка трекера",
count: 4,
},
{
name: "Кинотеатр",
count: 4,
},
{
name: "Проект страхование",
count: 4,
},
],
projects: [],
projectBoard: {},
toggleTab: 1,
modalType: "",
boardLoader: false,
columnName: "",
columnId: 0
};
export const setProjectBoardFetch = createAsyncThunk("userInfo", (id) =>
apiRequest(`/project/get-project?project_id=${id}&expand=columns`)
);
export const projectsTrackerSlice = createSlice({
name: "projectsTracker",
initialState,
reducers: {
setAllProjects: (state, action) => {
state.projects = action.payload;
},
setProject: (state, action) => {
state.project.push(action.payload);
state.projects.push(action.payload);
},
setToggleTab: (state, action) => {
state.toggleTab = action.payload;
},
deleteProject: (state, action) => {
state.projects.forEach((project) => {
if (project.id === action.payload.id) {
project.status = 10;
}
});
},
activeLoader: (state) => {
state.boardLoader = true;
},
moveProjectTask: (state, action) => {
state.projectBoard.columns.forEach((column, index) => {
if (column.id === action.payload.columnId) {
column.tasks.push(action.payload.startWrapperIndex.task);
apiRequest(`/task/update-task`, {
method: "PUT",
data: {
task_id: action.payload.startWrapperIndex.task.id,
column_id: column.id,
},
}).then((res) => {});
}
if (column.id === action.payload.startWrapperIndex.index) {
state.projectBoard.columns[index].tasks = column.tasks.filter(
(task) => task.id !== action.payload.startWrapperIndex.task.id
);
}
});
},
setColumnName: (state, action) => {
state.columnName = action.payload
},
setColumnId: (state, action) => {
state.columnId = action.payload
},
editProjectName: (state, action) => {
state.projects.forEach((project) => {
if (project.id === action.payload.id) {
project.name = action.payload.name;
}
});
},
editColumnName: (state, action) => {
state.projectBoard.columns.forEach((column) => {
if (column.id === action.payload.id) {
column.title = action.payload.title
}
})
},
modalToggle: (state, action) => {
state.modalType = action.payload;
},
},
extraReducers: {
[setProjectBoardFetch.fulfilled]: (state, action) => {
state.projectBoard = action.payload;
state.boardLoader = false;
},
},
});
export const { setProject } = projectsTrackerSlice.actions;
export const {
setProject,
setColumnName,
deleteProject,
setAllProjects,
moveProjectTask,
setToggleTab,
modalToggle,
activeLoader,
editProjectName,
editColumnName,
setColumnId
} = projectsTrackerSlice.actions;
export const getProjects = (state) => state.tracker.project;
export const getProjects = (state) => state.tracker.projects;
export const getProjectBoard = (state) => state.tracker.projectBoard;
export const getToggleTab = (state) => state.tracker.toggleTab;
export const getValueModalType = (state) => state.tracker.modalType;
export const getBoarderLoader = (state) => state.tracker.boardLoader;
export const getColumnName = (state) => state.tracker.columnName;
export const getColumnId = (state) => state.tracker.columnId;
export default projectsTrackerSlice.reducer;

View File

@ -1,13 +1,14 @@
import { createSlice } from '@reduxjs/toolkit';
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
dateSelected: '',
reportDate: '',
requestDates: ''
dateSelected: "",
reportDate: "",
requestDates: "",
sendRequest: "",
};
export const reportSlice = createSlice({
name: 'report',
name: "report",
initialState,
reducers: {
dateSelected: (state, action) => {
@ -17,17 +18,23 @@ export const reportSlice = createSlice({
state.reportDate = action.payload;
},
setRequestDate: (state, action) => {
state.requestDates = action.payload
}
state.requestDates = action.payload;
},
setSendRequest: (state, action) => {
state.sendRequest = action.payload;
},
},
});
export const { dateSelected, setReportDate, setRequestDate} = reportSlice.actions;
export const { dateSelected, setReportDate, setRequestDate, setSendRequest } =
reportSlice.actions;
export const selectDate = (state) => state.report.dateSelected;
export const getReportDate = (state) => state.report.reportDate;
export const getRequestDates = (state) => state.report.requestDates
export const getRequestDates = (state) => state.report.requestDates;
export const getSendRequest = (state) => state.report.sendRequest;
export default reportSlice.reducer;