Fixed copmonents

This commit is contained in:
MaxOvs19 2023-05-24 15:34:43 +03:00
parent 08f7d13f01
commit 2ad78834f3
41 changed files with 837 additions and 847 deletions

View File

@ -1,7 +1,7 @@
const path = require('path'); const path = require("path");
module.exports = { module.exports = {
public: path.resolve(__dirname, '../public'), public: path.resolve(__dirname, "../public"),
src: path.resolve(__dirname, '../src'), src: path.resolve(__dirname, "../src"),
build: path.resolve(__dirname, '../build'), build: path.resolve(__dirname, "../build"),
'@node_modules': path.resolve(__dirname, '../node_modules'), "@node_modules": path.resolve(__dirname, "../node_modules"),
}; };

View File

@ -1,10 +1,10 @@
const { merge } = require('webpack-merge'); const { merge } = require("webpack-merge");
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer') const BundleAnalyzerPlugin =
.BundleAnalyzerPlugin; require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const prod = require('./prod'); const prod = require("./prod");
module.exports = merge(prod, { module.exports = merge(prod, {
plugins: [new BundleAnalyzerPlugin()] plugins: [new BundleAnalyzerPlugin()],
}); });

View File

@ -1,14 +1,14 @@
const paths = require('../paths'); const paths = require("../paths");
const webpack = require('webpack'); const webpack = require("webpack");
const {merge} = require('webpack-merge'); const { merge } = require("webpack-merge");
const common = require('./common'); const common = require("./common");
module.exports = merge(common, { module.exports = merge(common, {
target : 'web', target: "web",
mode: 'development', mode: "development",
devtool: 'eval-cheap-source-map', devtool: "eval-cheap-source-map",
devServer: { devServer: {
compress: true, compress: true,
@ -17,7 +17,6 @@ module.exports = merge(common, {
historyApiFallback: true, historyApiFallback: true,
// open: true, // open: true,
port: 3000, port: 3000,
}, },
plugins: [new webpack.HotModuleReplacementPlugin()] plugins: [new webpack.HotModuleReplacementPlugin()],
}); });

View File

@ -1,62 +1,59 @@
const paths = require('../paths'); const paths = require("../paths");
const {merge} = require('webpack-merge'); const { merge } = require("webpack-merge");
const common = require('./common'); const common = require("./common");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = merge(common, { module.exports = merge(common, {
mode: 'production', mode: "production",
target :'browserslist', target: "browserslist",
entry: { entry: {
index: { index: {
import: `${paths.src}/index.js`, import: `${paths.src}/index.js`,
dependOn: ['react', 'helpers'] dependOn: ["react", "helpers"],
},
react: ['react', 'react-dom', 'prop-types'],
helpers: ['immer', 'nanoid']
}, },
devtool: false, react: ["react", "react-dom", "prop-types"],
output: { helpers: ["immer", "nanoid"],
filename: 'js/[name].[hash:8].bundle.js', },
publicPath: '/', devtool: false,
assetModuleFilename: '[hash][ext][query]' output: {
}, filename: "js/[name].[hash:8].bundle.js",
module: { publicPath: "/",
rules: [ assetModuleFilename: "[hash][ext][query]",
{ },
test: /\.(c|sa|sc)ss$/i, module: {
use: [ rules: [
MiniCssExtractPlugin.loader, {
{ test: /\.(c|sa|sc)ss$/i,
loader: 'css-loader', use: [
options: {importLoaders: 1} MiniCssExtractPlugin.loader,
}, {
'postcss-loader', loader: "css-loader",
'sass-loader' options: { importLoaders: 1 },
] },
}, "postcss-loader",
{ "sass-loader",
test: /\.(jpe?g|png|gif|svg)$/i, ],
type: 'asset/resource' },
// type: 'asset' {
}, test: /\.(jpe?g|png|gif|svg|webp)$/i,
type: "asset/resource",
] // type: 'asset'
}, },
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
chunkFilename: '[id].css'
}),
], ],
optimization: { },
runtimeChunk: 'single' plugins: [
}, new MiniCssExtractPlugin({
performance: { filename: "[name].[contenthash].css",
hints: 'warning', chunkFilename: "[id].css",
maxEntrypointSize: 512000, }),
maxAssetSize: 512000 ],
} optimization: {
runtimeChunk: "single",
},
performance: {
hints: "warning",
maxEntrypointSize: 512000,
maxAssetSize: 512000,
},
}); });

View File

@ -1,19 +1,19 @@
import React from 'react' import React from "react";
import './achievement.scss' import "./achievement.scss";
export const Achievement = ({ achievement }) => { export const Achievement = ({ achievement }) => {
return ( return (
<div className='achievement'> <div className="achievement">
<div className='achievement__icon'> <div className="achievement__icon">
<img src={achievement.img} alt='achievement' /> <img src={achievement.img} alt="achievement" />
</div> </div>
<div className='achievement__popup'> <div className="achievement__popup">
<div className='achievement__title'>{achievement.title}</div> <div className="achievement__title">{achievement.title}</div>
<div className='achievement__description'> <div className="achievement__description">
{achievement.description} {achievement.description}
</div> </div>
</div> </div>
</div> </div>
) );
} };

View File

@ -3,7 +3,7 @@ import { calendarHelper, currentMonthAndDay } from "./calendarHelper";
import ellipse from "../../images/ellipse.png"; import ellipse from "../../images/ellipse.png";
import rectangle from "../../images/rectangle__calendar.png"; import rectangle from "../../images/rectangle__calendar.png";
import calendarIcon from "../../images/calendar_icon.png"; import calendarIcon from "../../images/calendar.svg";
import moment from "moment"; import moment from "moment";
import "moment/locale/ru"; import "moment/locale/ru";

View File

@ -18,7 +18,6 @@ import { apiRequest } from "../../api/request";
import { createMarkup } from "../../helper"; import { createMarkup } from "../../helper";
import gitImgItem from "../../images/gitItemImg.svg"; import gitImgItem from "../../images/gitItemImg.svg";
import rectangle from "../../images/rectangle_secondPage.png"; import rectangle from "../../images/rectangle_secondPage.png";
import front from "../../images/front-end.webp"; import front from "../../images/front-end.webp";
import back from "../../images/back-end.webp"; import back from "../../images/back-end.webp";

View File

@ -1,73 +1,73 @@
.control-card{ .control-card {
max-width: 353px; max-width: 353px;
width: 100%; width: 100%;
padding: 35px 45px 15px 30px; padding: 35px 45px 15px 30px;
background: #FFFFFF; background: #ffffff;
border-radius: 12px; border-radius: 12px;
text-decoration: none; text-decoration: none;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.3s ease;
&:hover { &:hover {
box-shadow: 6px 5px 20px rgb(87 98 80 / 21%); box-shadow: 6px 5px 20px rgb(87 98 80 / 21%);
transform: scale(1.02); transform: scale(1.02);
} }
@media (max-width: 1175px) { @media (max-width: 1175px) {
width: 48%; width: 48%;
max-width: none; max-width: none;
} }
@media (max-width: 925px) { @media (max-width: 925px) {
width: 100%; width: 100%;
padding: 15px 25px; padding: 15px 25px;
} }
&__about { &__about {
display: flex; display: flex;
column-gap: 20px; column-gap: 20px;
align-items: center; align-items: center;
margin-bottom: 30px; margin-bottom: 30px;
@media (max-width: 925px) { @media (max-width: 925px) {
margin-bottom: 15px; margin-bottom: 15px;
} }
h3 { h3 {
color: #000000; color: #000000;
font-weight: 500; font-weight: 500;
font-size: 18px; font-size: 18px;
line-height: 22px; line-height: 22px;
max-width: 125px; max-width: 125px;
margin-bottom: 0; margin-bottom: 0;
} }
} }
&__info { &__info {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
p { p {
font-weight: 700; font-weight: 700;
font-size: 12px; font-size: 12px;
line-height: 20px; line-height: 20px;
color: #000000; color: #000000;
margin-bottom: 0; margin-bottom: 0;
span { span {
color: #52B709; color: #52b709;
font-weight: 700; font-weight: 700;
} }
} }
&Link { &Link {
width: 48px; width: 48px;
height: 48px; height: 48px;
background: #DDEEC6; background: #ddeec6;
border-radius: 50px; border-radius: 50px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
} }
} }

View File

@ -2,7 +2,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 33px 32px 25px 28px; padding: 33px 32px 25px 28px;
background: #FFFFFF; background: #ffffff;
border-radius: 12px; border-radius: 12px;
transition: all 0.3s ease; transition: all 0.3s ease;
position: relative; position: relative;
@ -18,7 +18,6 @@
pointer-events: none; pointer-events: none;
} }
&__title { &__title {
display: flex; display: flex;
align-items: center; align-items: center;
@ -43,7 +42,7 @@
p { p {
max-width: 181px; max-width: 181px;
margin-bottom: 0; margin-bottom: 0;
color: #6F6F6F; color: #6f6f6f;
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
line-height: 20px; line-height: 20px;
@ -55,7 +54,7 @@
justify-content: center; justify-content: center;
width: 48px; width: 48px;
height: 48px; height: 48px;
background: #DDEEC6; background: #ddeec6;
border-radius: 50px; border-radius: 50px;
} }
} }

View File

@ -9,9 +9,8 @@ import { selectProfiles } from "../../redux/outstaffingSlice";
import { urlForLocal } from "../../helper"; import { urlForLocal } from "../../helper";
import male from "../../images/medium_male.png";
import rectangle from "../../images/rectangle_secondPage.png"; import rectangle from "../../images/rectangle_secondPage.png";
import cursorImg from "../../images/cursorImg.png"; import cursorImg from "../../images/cursorImg.svg";
import "./description.scss"; import "./description.scss";

View File

@ -1,49 +0,0 @@
import React from 'react'
import logo from '../../images/logoGuild.png'
import vk from '../../images/vkLogo.svg'
import tg from '../../images/tgFooter.png'
import email from '../../images/emailLogo.svg'
import './footer.scss'
export const Footer = () => {
return (
<footer>
<div className='container'>
<div className='footer'>
<div className='footer__top'>
<img src={logo} alt='logo' />
<p>Подберем и документально оформим IT-специалистов, после чего передадим исполнителей под ваше руководство.
Вы получаете полное управление над сотрудниками, имея возможность контролировать и заменять IT штат.</p>
<div className='footer__copyright'>
© {new Date().getFullYear()} - Все права защищены
</div>
</div>
<div className='footer__bottom'>
<div className='footer__social'>
<div className='footer__social__icons'>
<a>
<img src={vk} alt='vk' />
</a>
<a>
<img src={tg} alt='tg' />
</a>
</div>
<p>Войти в команду</p>
</div>
<div className='footer__info'>
<div className='footer__mail'>
<a>
<img src={email} alt='email' />
</a>
<p>office@itguild.info</p>
</div>
<a className='footer__policy'>Политика конфиденциальности</a>
</div>
</div>
</div>
</div>
</footer>
)
}

View File

@ -0,0 +1,53 @@
import React from "react";
import logo from "../../images/logoGuild.png";
import vk from "../../images/vkLogo.svg";
import tg from "../../images/tgFooter.png";
import email from "../../images/emailLogo.svg";
import "./footer.scss";
export const Footer = () => {
return (
<footer>
<div className="container">
<div className="footer">
<div className="footer__top">
<img src={logo} alt="logo" />
<p>
Подберем и документально оформим IT-специалистов, после чего
передадим исполнителей под ваше руководство. Вы получаете полное
управление над сотрудниками, имея возможность контролировать и
заменять IT штат.
</p>
<div className="footer__copyright">
© {new Date().getFullYear()} - Все права защищены
</div>
</div>
<div className="footer__bottom">
<div className="footer__social">
<div className="footer__social__icons">
<a>
<img src={vk} alt="vk" />
</a>
<a>
<img src={tg} alt="tg" />
</a>
</div>
<p>Войти в команду</p>
</div>
<div className="footer__info">
<div className="footer__mail">
<a>
<img src={email} alt="email" />
</a>
<p>office@itguild.info</p>
</div>
<a className="footer__policy">Политика конфиденциальности</a>
</div>
</div>
</div>
</div>
</footer>
);
};

View File

@ -4,7 +4,6 @@ footer {
} }
.footer { .footer {
&__top { &__top {
display: flex; display: flex;
align-items: start; align-items: start;
@ -21,7 +20,7 @@ footer {
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
line-height: 16px; line-height: 16px;
color: #5B6871; color: #5b6871;
@media (max-width: 620px) { @media (max-width: 620px) {
margin-left: 0; margin-left: 0;
@ -82,7 +81,7 @@ footer {
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
line-height: 16px; line-height: 16px;
color: #5B6871; color: #5b6871;
} }
} }
@ -90,7 +89,7 @@ footer {
font-weight: 400; font-weight: 400;
font-size: 10px; font-size: 10px;
line-height: 16px; line-height: 16px;
color: #5B6871; color: #5b6871;
margin-left: 150px; margin-left: 150px;
@media (max-width: 720px) { @media (max-width: 720px) {
@ -98,7 +97,7 @@ footer {
} }
&:hover { &:hover {
color: #5B6871; color: #5b6871;
text-decoration: none; text-decoration: none;
} }
} }
@ -115,101 +114,4 @@ footer {
margin-left: 0; margin-left: 0;
} }
} }
//margin-top: -3rem;
//
//&__left {
// display: flex;
// align-items: center;
//}
//
//&__description {
// padding: 0 100px 0 34px;
//
// span {
// color: #18586e;
// font-family: 'GT Eesti Pro Display';
// font-size: 1.6em;
// font-weight: 400;
// font-style: normal;
// letter-spacing: normal;
// line-height: 16.81px;
// text-align: left;
// }
//}
//
//&__icon {
// text-align: end;
//
// img {
// margin-left: 20px;
// }
//}
//
//&__right {
// display: flex;
// flex-direction: column;
// align-items: left;
//}
//
//&__phone {
// color: #003b65;
// font-family: 'CeraPro';
// font-size: 2.1em;
// letter-spacing: normal;
// line-height: 25px;
// text-align: left;
//}
//
//&__working-hours {
// color: #003b65;
// font-family: 'CeraPro';
// font-size: 1.2em;
// font-weight: 400;
// font-style: normal;
// letter-spacing: normal;
// line-height: normal;
// margin-left: 24px;
//}
//
//&__copyright {
// padding: 1rem 1rem 1rem 5.6rem;
// font-family: 'Muller';
// font-weight: 300;
// font-size: 1.2em;
//}
} }
//@media (max-width: 1199px) {
// .footer {
// &__left {
// margin-bottom: 20px;
// }
// }
//}
//
//@media (max-width: 575.98px) {
// .footer {
// &__left {
// margin-top: 80px;
// }
//
// &__description {
// padding: 0;
// margin-left: 10px;
//
// span {
// font-size: 1.2em;
// }
// }
//
// &__icon {
// img {
// margin-left: 10px;
// }
// }
//
// &__right {
// margin-bottom: 20px;
// }
// }
//}

View File

@ -1,134 +0,0 @@
import React, {useEffect, useState} from 'react'
import {useParams, useNavigate} from 'react-router-dom'
import {Loader} from '../Loader/Loader'
import PhoneInput from 'react-phone-input-2'
import 'react-phone-input-2/lib/style.css'
import './form.scss'
import {apiRequest} from "../../api/request";
import Swal from 'sweetalert2'
import withReactContent from 'sweetalert2-react-content'
const SweetAlert = withReactContent(Swal);
const Form = () => {
const navigate = useNavigate();
const urlParams = useParams();
const [status, setStatus] = useState(null);
const [data, setData] = useState({
email: '',
phone: '',
comment: ''
});
const [isFetching, setIsFetching] = useState(false);
const handleModal = (status) => {
SweetAlert.fire({
text: status !== 200 || 201
? 'Какие-то неполадки =('
: 'Форма отправлена',
preConfirm: () =>
status !== 200 || 201 ? () => {
setStatus(null)
} : () => {
setStatus(null);
navigate(`/candidate/${urlParams.id}`)
}
});
};
useEffect(() => {
if (status) {
handleModal(status)
}
}, [status]);
const handleChange = (e) => {
const {id, value} = e.target;
setData((prev) => ({
...prev,
[id]: value
}))
};
const handleSubmit = (e) => {
e.preventDefault();
setIsFetching(true);
const formData = new FormData();
formData.append('profile_id', urlParams.id);
formData.append('Email', data.email);
formData.append('phone', data.phone);
formData.append('comment', data.comment);
apiRequest('/interview-request/create-interview-request', {
method: 'POST',
params: {
profile_id: urlParams.id,
...data
}
}).then((res) => {
setStatus(res);
setIsFetching(false)
}
)
};
return (
<div className='row'>
<div className='col-sm-12'>
<form className='form' id='test'>
<label htmlFor='email'>Емейл:</label>
<input
onChange={handleChange}
id='email'
name='Email'
type='email'
placeholder='Емейл'
value={data.email}
/>
<label htmlFor='phone'>Номер телефона:</label>
<PhoneInput
id='phone'
name='Phone'
country={'ru'}
value={data.phone}
onChange={(e) =>
handleChange({target: {value: e, id: 'phone'}})
}
/>
{/* <input
onChange={handleChange}
id="phone"
type="text"
name="Phone"
placeholder="Телефон"
value={data.phone}
/> */}
<textarea
onChange={handleChange}
id='comment'
rows='5'
cols='40'
name='Comment'
placeholder='Оставьте комментарий'
value={data.comment}
></textarea>
<button onClick={handleSubmit} className='form__btn' type='submit'>
{isFetching ? <Loader/> : 'Отправить'}
</button>
</form>
</div>
</div>
)
};
export default Form

View File

@ -0,0 +1,125 @@
import React, { useEffect, useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import PhoneInput from "react-phone-input-2";
import { apiRequest } from "../../api/request";
import { Loader } from "../Loader/Loader";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import "react-phone-input-2/lib/style.css";
import "./form.scss";
const SweetAlert = withReactContent(Swal);
const Form = () => {
const navigate = useNavigate();
const urlParams = useParams();
const [status, setStatus] = useState(null);
const [data, setData] = useState({
email: "",
phone: "",
comment: "",
});
const [isFetching, setIsFetching] = useState(false);
const handleModal = (status) => {
SweetAlert.fire({
text:
status !== 200 || 201 ? "Какие-то неполадки =(" : "Форма отправлена",
preConfirm: () =>
status !== 200 || 201
? () => {
setStatus(null);
}
: () => {
setStatus(null);
navigate(`/candidate/${urlParams.id}`);
},
});
};
useEffect(() => {
if (status) {
handleModal(status);
}
}, [status]);
const handleChange = (e) => {
const { id, value } = e.target;
setData((prev) => ({
...prev,
[id]: value,
}));
};
const handleSubmit = (e) => {
e.preventDefault();
setIsFetching(true);
const formData = new FormData();
formData.append("profile_id", urlParams.id);
formData.append("Email", data.email);
formData.append("phone", data.phone);
formData.append("comment", data.comment);
apiRequest("/interview-request/create-interview-request", {
method: "POST",
params: {
profile_id: urlParams.id,
...data,
},
}).then((res) => {
setStatus(res);
setIsFetching(false);
});
};
return (
<div className="row">
<div className="col-sm-12">
<form className="form" id="test">
<label htmlFor="email">Емейл:</label>
<input
onChange={handleChange}
id="email"
name="Email"
type="email"
placeholder="Емейл"
value={data.email}
/>
<label htmlFor="phone">Номер телефона:</label>
<PhoneInput
id="phone"
name="Phone"
country={"ru"}
value={data.phone}
onChange={(e) =>
handleChange({ target: { value: e, id: "phone" } })
}
/>
<textarea
onChange={handleChange}
id="comment"
rows="5"
cols="40"
name="Comment"
placeholder="Оставьте комментарий"
value={data.comment}
></textarea>
<button onClick={handleSubmit} className="form__btn" type="submit">
{isFetching ? <Loader /> : "Отправить"}
</button>
</form>
</div>
</div>
);
};
export default Form;

View File

@ -7,22 +7,22 @@
margin: 0 0 -5px 29px; margin: 0 0 -5px 29px;
} }
&__icon-question {
}
&__title { &__title {
font-weight: 700; font-weight: 700;
@include adaptiv-value("font-size", 28, 22, 1); @include adaptiv-value("font-size", 28, 22, 1);
line-height: 79%; line-height: 79%;
color: #1458dd; color: #1458dd;
} }
&__body { &__body {
position: relative; position: relative;
z-index: 2; z-index: 2;
display: block; display: block;
&:not(:last-child) { &:not(:last-child) {
margin: 0 0 13px 0; margin: 0 0 13px 0;
} }
p { p {
word-break: break-word; word-break: break-word;
background: #ffffff; background: #ffffff;

View File

@ -1,15 +0,0 @@
import React from "react";
import {LogoutButton} from "../LogoutButton/LogoutButton";
import './header.scss'
export const Header = () => {
return (
<div className='container header'>
<h2>
<span>Аутстаффинг</span> it-персонала
</h2>
<LogoutButton/>
</div>
)
};

View File

@ -0,0 +1,16 @@
import React from "react";
import { LogoutButton } from "../LogoutButton/LogoutButton";
import "./header.scss";
export const Header = () => {
return (
<div className="container header">
<h2>
<span>Аутстаффинг</span> it-персонала
</h2>
<LogoutButton />
</div>
);
};

View File

@ -9,7 +9,7 @@
flex: 1; flex: 1;
text-align: center; text-align: center;
color: #52b709; color: #52b709;
font-family: 'GT Eesti Pro Display', sans-serif; font-family: "GT Eesti Pro Display", sans-serif;
font-size: 5em; font-size: 5em;
font-weight: 700; font-weight: 700;
font-style: normal; font-style: normal;

View File

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

View File

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

View File

@ -5,6 +5,7 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
position: relative; position: relative;
&:hover { &:hover {
path { path {
fill: #6aaf5c; fill: #6aaf5c;

View File

@ -1,33 +0,0 @@
import React, {useState} from 'react'
import {useNavigate} from 'react-router-dom'
import {useSelector} from 'react-redux'
import {useLogout} from "../../hooks/useLogout";
import {Loader} from '../Loader/Loader'
import {getRole} from '../../redux/roleSlice'
import './logoutButton.scss'
export const LogoutButton = () => {
const [isLoggingOut, setIsLoggingOut] = useState(false);
const userRole = useSelector(getRole);
const navigate = useNavigate();
const {logout} = useLogout();
return (
<button
className='logout-button'
onClick={() => {
setIsLoggingOut(true);
logout();
setIsLoggingOut(false);
navigate(userRole === 'ROLE_DEV' ? '/authdev' : '/auth')
}}
>
{isLoggingOut ? <Loader/> : 'Выйти'}
</button>
)
};

View File

@ -0,0 +1,31 @@
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useSelector } from "react-redux";
import { useLogout } from "../../hooks/useLogout";
import { Loader } from "../Loader/Loader";
import { getRole } from "../../redux/roleSlice";
import "./logoutButton.scss";
export const LogoutButton = () => {
const [isLoggingOut, setIsLoggingOut] = useState(false);
const userRole = useSelector(getRole);
const navigate = useNavigate();
const { logout } = useLogout();
return (
<button
className="logout-button"
onClick={() => {
setIsLoggingOut(true);
logout();
setIsLoggingOut(false);
navigate(userRole === "ROLE_DEV" ? "/authdev" : "/auth");
}}
>
{isLoggingOut ? <Loader /> : "Выйти"}
</button>
);
};

View File

@ -1,5 +1,4 @@
.logout-button { .logout-button {
position: relative; position: relative;
z-index: 100; z-index: 100;
display: flex; display: flex;
@ -14,7 +13,7 @@
background-color: #6aaf5c; background-color: #6aaf5c;
color: #ffffff; color: #ffffff;
border: 3px solid #6aaf5c; border: 3px solid #6aaf5c;
font-family: 'Muller', sans-serif; font-family: "Muller", sans-serif;
text-align: center; text-align: center;
&:hover { &:hover {

View File

@ -3,7 +3,6 @@ import { useSelector, useDispatch } from "react-redux";
import OutstaffingBlock from "../OutstaffingBlock/OutstaffingBlock"; import OutstaffingBlock from "../OutstaffingBlock/OutstaffingBlock";
import TagSelect from "../Select/TagSelect"; import TagSelect from "../Select/TagSelect";
import { import {
selectTags, selectTags,
getPositionId, getPositionId,

View File

@ -1,118 +0,0 @@
import React from 'react'
import OutsideClickHandler from 'react-outside-click-handler'
import {useDispatch, useSelector} from 'react-redux'
import {
selectItems,
selectedItems,
profiles,
} from '../../redux/outstaffingSlice'
import {apiRequest} from "../../api/request";
import './outstaffingBlock.scss'
const handlePositionClick = (
{
dispatch,
positionId,
isSelected,
onSelect,
apiRequest
}) => {
if (isSelected) {
apiRequest('/profile', {
params: {
limit: 1000
},
}).then((profileArr) => {
dispatch(profiles(profileArr));
dispatch(selectedItems([]));
onSelect(positionId)
})
} else {
apiRequest('/profile', {
params: {
limit: '1000',
position_id: positionId
},
}).then((res) => {
dispatch(profiles(res));
dispatch(selectedItems([]));
onSelect(positionId)
})
}
};
const OutstaffingBlock = (
{
dataTags = [],
selected,
img,
header,
positionId,
isSelected,
onSelect
}) => {
const dispatch = useDispatch();
const itemsArr = useSelector(selectItems);
const handleBlockClick = (item, id) => {
if (!itemsArr.find((el) => item === el.value)) {
dispatch(selectedItems([...itemsArr, {id, value: item, label: item}]))
}
};
let classes;
dataTags.forEach((el) => {
if (el.name === 'skills_back') {
classes = 'back'
} else if (el.name === 'skills_design') {
classes = 'des'
} else if (el.name === 'skills_front') {
classes = 'front'
}
});
return (
<OutsideClickHandler onOutsideClick={() => isSelected && onSelect(null)}>
<div className={`outstaffing-block${isSelected ? ' outstaffing-block__selected' : ''}`}>
<div className={`outstaffing-block__img ${selected ? ' outstaffing-block__border' : ''}`}
onClick={() => handlePositionClick(
{
dispatch,
positionId,
isSelected,
onSelect,
apiRequest
})
}>
<h3>{header}</h3>
<img className={classes} src={img} alt='img'/>
</div>
<div className={`${selected ? 'outstaffing-block__mobile--block' : 'outstaffing-block__mobile--none'}`} >
<p className='outstaffing-block__text'># Популярный стек</p>
{dataTags && (
<ul className='outstaffing-block__items'>
{dataTags.map((item) => (
<li
key={item.id}
onClick={() => handleBlockClick(item.value, item.id)}
>
{item.value}
</li>
))}
</ul>
)}
</div>
</div>
</OutsideClickHandler>
)
};
export default OutstaffingBlock

View File

@ -0,0 +1,126 @@
import React from "react";
import OutsideClickHandler from "react-outside-click-handler";
import { useDispatch, useSelector } from "react-redux";
import {
selectItems,
selectedItems,
profiles,
} from "../../redux/outstaffingSlice";
import { apiRequest } from "../../api/request";
import "./outstaffingBlock.scss";
const handlePositionClick = ({
dispatch,
positionId,
isSelected,
onSelect,
apiRequest,
}) => {
if (isSelected) {
apiRequest("/profile", {
params: {
limit: 1000,
},
}).then((profileArr) => {
dispatch(profiles(profileArr));
dispatch(selectedItems([]));
onSelect(positionId);
});
} else {
apiRequest("/profile", {
params: {
limit: "1000",
position_id: positionId,
},
}).then((res) => {
dispatch(profiles(res));
dispatch(selectedItems([]));
onSelect(positionId);
});
}
};
const OutstaffingBlock = ({
dataTags = [],
selected,
img,
header,
positionId,
isSelected,
onSelect,
}) => {
const dispatch = useDispatch();
const itemsArr = useSelector(selectItems);
const handleBlockClick = (item, id) => {
if (!itemsArr.find((el) => item === el.value)) {
dispatch(selectedItems([...itemsArr, { id, value: item, label: item }]));
}
};
let classes;
dataTags.forEach((el) => {
if (el.name === "skills_back") {
classes = "back";
} else if (el.name === "skills_design") {
classes = "des";
} else if (el.name === "skills_front") {
classes = "front";
}
});
return (
<OutsideClickHandler onOutsideClick={() => isSelected && onSelect(null)}>
<div
className={`outstaffing-block${
isSelected ? " outstaffing-block__selected" : ""
}`}
>
<div
className={`outstaffing-block__img ${
selected ? " outstaffing-block__border" : ""
}`}
onClick={() =>
handlePositionClick({
dispatch,
positionId,
isSelected,
onSelect,
apiRequest,
})
}
>
<h3>{header}</h3>
<img className={classes} src={img} alt="img" />
</div>
<div
className={`${
selected
? "outstaffing-block__mobile--block"
: "outstaffing-block__mobile--none"
}`}
>
<p className="outstaffing-block__text"># Популярный стек</p>
{dataTags && (
<ul className="outstaffing-block__items">
{dataTags.map((item) => (
<li
key={item.id}
onClick={() => handleBlockClick(item.value, item.id)}
>
{item.value}
</li>
))}
</ul>
)}
</div>
</div>
</OutsideClickHandler>
);
};
export default OutstaffingBlock;

View File

@ -1,15 +1,15 @@
body { body {
font-family: 'LabGrotesque', sans-serif !important; font-family: "LabGrotesque", sans-serif !important;
} }
.container { .container {
max-width: 1160px !important; max-width: 1160px !important;
} }
.catalog { .catalog {
background: #F1F1F1; background: #f1f1f1;
height: 100%; height: 100%;
min-height: 100vh; min-height: 100vh;
font-family: 'LabGrotesque', sans-serif; font-family: "LabGrotesque", sans-serif;
padding-top: 23px; padding-top: 23px;
&__title { &__title {
@ -37,7 +37,7 @@ body {
} }
& > p { & > p {
font-family: 'GT Eesti Pro Display'; font-family: "GT Eesti Pro Display";
font-size: 1.2em; font-size: 1.2em;
font-weight: 300; font-weight: 300;
font-style: normal; font-style: normal;
@ -60,7 +60,7 @@ body {
right: 13%; right: 13%;
top: 30%; top: 30%;
max-width: 130px; max-width: 130px;
font-family: 'GT Eesti Pro Display'; font-family: "GT Eesti Pro Display";
font-size: 18px; font-size: 18px;
font-weight: 400; font-weight: 400;
font-style: normal; font-style: normal;
@ -85,7 +85,7 @@ body {
&__items { &__items {
li { li {
font-family: 'GT Eesti Pro Display'; font-family: "GT Eesti Pro Display";
font-size: 1.8em; font-size: 1.8em;
font-weight: 100; font-weight: 100;
font-style: normal; font-style: normal;

View File

@ -1,16 +0,0 @@
import React from 'react'
import {Link} from "react-router-dom";
import './profileBreadcrumbs.scss'
export const ProfileBreadcrumbs = ({ links }) => {
return (
<div className='profileBreadcrumbs'>
{links.map((link, index) => {
return <Link key={index} to={link.link}>{link.name}</Link>
})
}
</div>
)
}

View File

@ -0,0 +1,18 @@
import React from "react";
import { Link } from "react-router-dom";
import "./profileBreadcrumbs.scss";
export const ProfileBreadcrumbs = ({ links }) => {
return (
<div className="profileBreadcrumbs">
{links.map((link, index) => {
return (
<Link key={index} to={link.link}>
{link.name}
</Link>
);
})}
</div>
);
};

View File

@ -7,7 +7,7 @@
} }
a { a {
color: #5B6871; color: #5b6871;
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
line-height: 16px; line-height: 16px;
@ -31,11 +31,11 @@
} }
&:after { &:after {
content: ''; content: "";
background-image: url("../../images/BreadcrumbsArrow.png"); background-image: url("../../images/BreadcrumbsArrow.png");
background-repeat: no-repeat; background-repeat: no-repeat;
width: 7px; width: 7px;
height:10px; height: 10px;
position: absolute; position: absolute;
right: -14px; right: -14px;
} }

View File

@ -1,20 +1,16 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { Link, Navigate } from "react-router-dom";
import { getReports } from "../Calendar/calendarHelper"; import { getReports } from "../Calendar/calendarHelper";
import { Link, Navigate } from "react-router-dom";
import moment from "moment"; import moment from "moment";
import { ProfileCalendarComponent } from "./ProfileCalendarComponent"; import { ProfileCalendarComponent } from "./ProfileCalendarComponent";
import { Loader } from "../Loader/Loader"; import { Loader } from "../Loader/Loader";
import { ProfileHeader } from "../ProfileHeader/ProfileHeader"; import { ProfileHeader } from "../ProfileHeader/ProfileHeader";
import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs"; import { ProfileBreadcrumbs } from "../ProfileBreadcrumbs/ProfileBreadcrumbs";
import { Footer } from "../Footer/Footer"; import { Footer } from "../Footer/Footer";
import { Navigation } from "../Navigation/Navigation"; 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 { apiRequest } from "../../api/request";
import { getProfileInfo } from "../../redux/outstaffingSlice"; import { getProfileInfo } from "../../redux/outstaffingSlice";
import { import {

View File

@ -1,23 +1,25 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import arrow from "../../images/arrowCalendar.png"; import { useDispatch } from "react-redux";
import rectangle from "../../images/rectangle__calendar.png"; import { Link } from "react-router-dom";
import calendarIcon from "../../images/calendar_icon.png";
import moment from "moment"; import {
setReportDate,
setRequestDate,
setSendRequest,
} from "../../redux/reportSlice";
import { import {
calendarHelper, calendarHelper,
currentMonthAndDay, currentMonthAndDay,
getReports, getReports,
hourOfNum, hourOfNum,
} from "../Calendar/calendarHelper"; } 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 ShortReport from "../ShortReport/ShortReport";
import arrow from "../../images/arrowCalendar.png";
import rectangle from "../../images/rectangle__calendar.png";
import calendarIcon from "../../images/calendar.svg";
import moment from "moment";
import "moment/locale/ru"; import "moment/locale/ru";
import "./../Calendar/calendarComponent.scss"; import "./../Calendar/calendarComponent.scss";

View File

@ -1,81 +1,100 @@
import React, {useState, useEffect} from 'react' import React, { useState, useEffect } from "react";
import {useSelector} from 'react-redux' import { useSelector } from "react-redux";
import {Link, Navigate, useNavigate} from 'react-router-dom' import { Link, Navigate, useNavigate } from "react-router-dom";
import DatePicker, { registerLocale } from "react-datepicker" import DatePicker, { registerLocale } from "react-datepicker";
import {getCorrectDate, getCreatedDate, hourOfNum} from '../Calendar/calendarHelper' import {
import ru from "date-fns/locale/ru" getCorrectDate,
getCreatedDate,
hourOfNum,
} from "../Calendar/calendarHelper";
import ru from "date-fns/locale/ru";
registerLocale("ru", ru); registerLocale("ru", ru);
import {Loader} from '../Loader/Loader' import { Loader } from "../Loader/Loader";
import {Footer} from "../Footer/Footer"; import { Footer } from "../Footer/Footer";
import {ProfileHeader} from "../ProfileHeader/ProfileHeader"; import { ProfileHeader } from "../ProfileHeader/ProfileHeader";
import {ProfileBreadcrumbs} from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs" import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs";
import {apiRequest} from "../../api/request"; import { apiRequest } from "../../api/request";
import {getReportDate} from '../../redux/reportSlice' import { getReportDate } from "../../redux/reportSlice";
import calendarIcon from '../../images/calendar_icon.png' import calendarIcon from "../../images/calendar.svg";
import ellipse from '../../images/ellipse.png' import ellipse from "../../images/ellipse.png";
import remove from '../../images/remove.png' import remove from "../../images/remove.png";
import arrow from "../../images/right-arrow.png"; import arrow from "../../images/right-arrow.png";
import './reportForm.scss' import "./reportForm.scss";
import "react-datepicker/dist/react-datepicker.css"; import "react-datepicker/dist/react-datepicker.css";
import { Navigation } from '../Navigation/Navigation' import { Navigation } from "../Navigation/Navigation";
const ReportForm = () => { const ReportForm = () => {
if(localStorage.getItem('role_status') === '18') { if (localStorage.getItem("role_status") === "18") {
return <Navigate to="/profile" replace/> return <Navigate to="/profile" replace />;
} }
const navigate= useNavigate(); const navigate = useNavigate();
const reportDate = useSelector(getReportDate); const reportDate = useSelector(getReportDate);
useEffect(() => { useEffect(() => {
initListeners() initListeners();
}, []) }, []);
const [isFetching, setIsFetching] = useState(false); const [isFetching, setIsFetching] = useState(false);
const [reportSuccess, setReportSuccess] = useState(''); const [reportSuccess, setReportSuccess] = useState("");
const [startDate, setStartDate] = useState(reportDate ? new Date (reportDate._d) : new Date()); const [startDate, setStartDate] = useState(
reportDate ? new Date(reportDate._d) : new Date()
);
const [datePickerOpen, setDatePickerOpen] = useState(false); const [datePickerOpen, setDatePickerOpen] = useState(false);
const [inputs, setInputs] = useState([{task: '', hours_spent: '', minutes_spent: 0}]); const [inputs, setInputs] = useState([
const [troublesInputValue, setTroublesInputValue] = useState(''); { task: "", hours_spent: "", minutes_spent: 0 },
const [scheduledInputValue, setScheduledInputValue] = useState(''); ]);
const [troublesInputValue, setTroublesInputValue] = useState("");
const [scheduledInputValue, setScheduledInputValue] = useState("");
const addInput = () => { const addInput = () => {
setInputs((prev) => [...prev, {task: '', hours_spent: '', minutes_spent: 0}]) setInputs((prev) => [
...prev,
{ task: "", hours_spent: "", minutes_spent: 0 },
]);
}; };
const initListeners = () => { const initListeners = () => {
document.addEventListener('click', closeByClickingOut) document.addEventListener("click", closeByClickingOut);
} };
const closeByClickingOut = (event) => { const closeByClickingOut = (event) => {
const path = event.path || (event.composedPath && event.composedPath()) const path = event.path || (event.composedPath && event.composedPath());
if (event && !path.find((div) => div.classList && (div.classList.contains('report-form__block-img') || div.classList.contains('react-datepicker-popper')))) { if (
setDatePickerOpen(false) event &&
!path.find(
(div) =>
div.classList &&
(div.classList.contains("report-form__block-img") ||
div.classList.contains("react-datepicker-popper"))
)
) {
setDatePickerOpen(false);
} }
} };
const totalHours = inputs.reduce((a, b) => a + b.hours_spent, 0); const totalHours = inputs.reduce((a, b) => a + b.hours_spent, 0);
const deleteInput = (indexRemove) => { const deleteInput = (indexRemove) => {
setInputs((prev) => prev.filter((el, index) => index !== indexRemove)) setInputs((prev) => prev.filter((el, index) => index !== indexRemove));
}; };
const handler = () => { const handler = () => {
for (let input of inputs) { for (let input of inputs) {
if(!input.task || !input.hours_spent) { if (!input.task || !input.hours_spent) {
setReportSuccess('Заполните задачи'); setReportSuccess("Заполните задачи");
setTimeout(() => setReportSuccess(''), 2000) setTimeout(() => setReportSuccess(""), 2000);
return return;
} }
} }
apiRequest('/reports/create', { apiRequest("/reports/create", {
method: 'POST', method: "POST",
data: { data: {
tasks: inputs, tasks: inputs,
difficulties: troublesInputValue, difficulties: troublesInputValue,
@ -84,156 +103,223 @@ const ReportForm = () => {
status: 1, status: 1,
}, },
}).then((res) => { }).then((res) => {
setReportSuccess('Отчет отправлен'); setReportSuccess("Отчет отправлен");
setTimeout(() => { setTimeout(() => {
setReportSuccess('') setReportSuccess("");
navigate('/profile/calendar'); navigate("/profile/calendar");
}, 1000) }, 1000);
setInputs(() => []); setInputs(() => []);
setTroublesInputValue(''); setTroublesInputValue("");
setScheduledInputValue(''); setScheduledInputValue("");
setIsFetching(false); setIsFetching(false);
setInputs(() => [{task: '', hours_spent: '', minutes_spent: 0}]); setInputs(() => [{ task: "", hours_spent: "", minutes_spent: 0 }]);
}) });
}; };
return ( return (
<section className='report-form'> <section className="report-form">
<ProfileHeader/> <ProfileHeader />
<Navigation /> <Navigation />
<div className='container'> <div className="container">
<ProfileBreadcrumbs links={[{name: 'Главная', link: '/profile'}, <ProfileBreadcrumbs
{name: 'Ваша отчетность', link: '/profile/calendar'}, links={[
{name: 'Страница добавления нового отчета', link: '/report'}]} { name: "Главная", link: "/profile" },
/> { name: "Ваша отчетность", link: "/profile/calendar" },
<h2 className='summary__title'>Ваши отчеты - <span>добавить отчет</span></h2> { name: "Страница добавления нового отчета", link: "/report" },
<div> ]}
<div className='report__head'> />
<Link className='calendar__back' to={`/profile/calendar`}> <h2 className="summary__title">
<img src={arrow} alt=''/><p>Вернуться</p> Ваши отчеты - <span>добавить отчет</span>
</Link> </h2>
</div> <div>
<div className="report__head">
<Link className="calendar__back" to={`/profile/calendar`}>
<img src={arrow} alt="" />
<p>Вернуться</p>
</Link>
</div> </div>
</div>
<div className='report-form__content'> <div className="report-form__content">
<div className='report-form__block'> <div className="report-form__block">
<div className='report-form__block-title'> <div className="report-form__block-title">
<h2>Добавление отчета за день</h2> <h2>Добавление отчета за день</h2>
<h3>Дата заполнения отчета:</h3> <h3>Дата заполнения отчета:</h3>
</div> </div>
<div className='report-form__block-img' onClick={() => setDatePickerOpen(true)}> <div
<img className="report-form__block-img"
className='report-form__calendar-icon' onClick={() => setDatePickerOpen(true)}
src={calendarIcon} >
alt='' <img
/> className="report-form__calendar-icon"
{getCorrectDate(startDate)} src={calendarIcon}
</div> alt=""
<DatePicker
className='datePicker'
open={datePickerOpen}
locale="ru"
selected={startDate}
onChange={(date) => {
setDatePickerOpen(false)
setStartDate(date)
}}
/> />
<div className='report-form__task-list'> {getCorrectDate(startDate)}
<img src={ellipse} alt=''/>
<span>Какие задачи были выполнены?</span>
</div>
</div> </div>
<DatePicker
className="datePicker"
open={datePickerOpen}
locale="ru"
selected={startDate}
onChange={(date) => {
setDatePickerOpen(false);
setStartDate(date);
}}
/>
<div className="report-form__task-list">
<img src={ellipse} alt="" />
<span>Какие задачи были выполнены?</span>
</div>
</div>
<div className='row'> <div className="row">
<div className='col-8'> <div className="col-8">
<div className='report-form__task-header'> <div className="report-form__task-header">
<p className='report-form__task-title--description'> <p className="report-form__task-title--description">
Краткое описание задачи Краткое описание задачи
</p> </p>
<p className='report-form__task-title--hours'>Количество часов</p> <p className="report-form__task-title--hours">
</div> Количество часов
</p>
</div>
{inputs.map((input, index) => { {inputs.map((input, index) => {
return ( return (
<form id={'input'} key={`input__${index}`} className='report-form__task-form'> <form
<div className='report-form__task-number'> id={"input"}
{index + 1}. key={`input__${index}`}
</div> className="report-form__task-form"
<div className='report-form__task-input report-form__task-input--description'> >
<input value={inputs[index].task} className={!input.task && reportSuccess === 'Заполните задачи' ? 'checkTask' : ''} name='text' type='text' <div className="report-form__task-number">{index + 1}.</div>
onChange={e => setInputs(inputs.map((input, inputIndex) => { <div className="report-form__task-input report-form__task-input--description">
return index === inputIndex <input
? { value={inputs[index].task}
...input, className={
task: e.target.value !input.task && reportSuccess === "Заполните задачи"
} ? "checkTask"
: input : ""
}))}/>
</div>
<div className='report-form__task-input report-form__task-input--hours'>
<input value={inputs[index].hours_spent} className={!input.hours_spent && reportSuccess === 'Заполните задачи' ? 'checkTask' : ''} name='number' type='number' min='1'
onChange={e => setInputs(inputs.map((input, inputIndex) => {
return index === inputIndex
? {
...input,
hours_spent: Number(e.target.value)
}
: input
}))}/>
</div>
{index > 0 &&
<div className='report-form__task-remove'>
<img onClick={() => deleteInput(index)} src={remove} alt=''/>
</div>
} }
</form> name="text"
) type="text"
})} onChange={(e) =>
setInputs(
inputs.map((input, inputIndex) => {
return index === inputIndex
? {
...input,
task: e.target.value,
}
: input;
})
)
}
/>
</div>
<div className="report-form__task-input report-form__task-input--hours">
<input
value={inputs[index].hours_spent}
className={
!input.hours_spent &&
reportSuccess === "Заполните задачи"
? "checkTask"
: ""
}
name="number"
type="number"
min="1"
onChange={(e) =>
setInputs(
inputs.map((input, inputIndex) => {
return index === inputIndex
? {
...input,
hours_spent: Number(e.target.value),
}
: input;
})
)
}
/>
</div>
{index > 0 && (
<div className="report-form__task-remove">
<img
onClick={() => deleteInput(index)}
src={remove}
alt=""
/>
</div>
)}
</form>
);
})}
<div className='report-form__form-add'> <div className="report-form__form-add">
<p className='addMore' onClick={addInput}>+</p> <p className="addMore" onClick={addInput}>
<span>Добавить еще </span> +
</p>
<span>Добавить еще </span>
</div>
</div>
</div>
<div className="row">
<div className="col-12">
<div className="report-form__input-box">
<div className="report-form__troubles">
<img src={ellipse} alt="" />
<span>Какие сложности возникли?</span>
</div> </div>
<input
type="text"
value={troublesInputValue}
onChange={(e) => setTroublesInputValue(e.target.value)}
/>
<div className="report-form__scheduled">
<img src={ellipse} alt="" />
<span>Что планируется сделать завтра?</span>
</div>
<input
type="text"
value={scheduledInputValue}
onChange={(e) => setScheduledInputValue(e.target.value)}
/>
</div> </div>
</div> </div>
</div>
<div className='row'> <div className="row">
<div className='col-12'> <div className="col-12">
<div className='report-form__input-box'> <div className="report-form__footer">
<div className='report-form__troubles'> <button
<img src={ellipse} alt=''/> className="report-form__footer-btn"
<span>Какие сложности возникли?</span> onClick={() => handler()}
>
{isFetching ? <Loader /> : "Отправить"}
</button>
<p className="report-form__footer-text">
Всего за день :{" "}
<span>
{totalHours} {hourOfNum(totalHours)}
</span>
</p>
{reportSuccess && (
<p
className={`report-form__footer-done ${
reportSuccess === "Заполните задачи" ? "errorText" : ""
}`}
>
{reportSuccess}
</p>
)}
</div> </div>
<input type='text' value={troublesInputValue} onChange={e => setTroublesInputValue(e.target.value)}/>
<div className='report-form__scheduled'>
<img src={ellipse} alt=''/>
<span>Что планируется сделать завтра?</span>
</div>
<input type='text' value={scheduledInputValue} onChange={e => setScheduledInputValue(e.target.value)}/>
</div> </div>
</div> </div>
</div> </div>
<div className='row'> </div>
<div className='col-12'> <Footer />
<div className='report-form__footer'> </section>
<button className='report-form__footer-btn' onClick={() => handler()}> );
{isFetching ? <Loader/> : 'Отправить'}
</button>
<p className='report-form__footer-text'>
Всего за день : <span>{totalHours} {hourOfNum(totalHours)}</span>
</p>
{reportSuccess &&
<p className={`report-form__footer-done ${reportSuccess === 'Заполните задачи' ? 'errorText' : ''}`}>{reportSuccess}</p>
}
</div>
</div>
</div>
</div>
</div>
<Footer/>
</section>
)
}; };
export default ReportForm export default ReportForm;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 645 B

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

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.278 23.9789L9.078 15.7389L4 20.2159V2.01887C3.99999 1.64616 4.10412 1.28087 4.30065 0.964186C4.49718 0.647505 4.77829 0.392041 5.11227 0.22661C5.44626 0.0611791 5.81982 -0.00763698 6.19083 0.0279244C6.56183 0.0634857 6.91552 0.20201 7.212 0.427871L21.117 12.4359L14.5 13.1699L18.645 21.2999L13.278 23.9789ZM9.684 12.5339L14.158 21.2999L15.947 20.4059L11.4 11.4999L16.338 10.9529L5.952 1.97987L5.994 15.7899L9.684 12.5339Z" fill="#374957"/>
</svg>

After

Width:  |  Height:  |  Size: 556 B

View File

@ -14,7 +14,7 @@ import { Footer } from "../../components/Footer/Footer";
import { apiRequest } from "../../api/request"; import { apiRequest } from "../../api/request";
import cursorImg from "../../images/cursorImg.png"; import cursorImg from "../../images/cursorImg.svg";
import "./partnerRequests.scss"; import "./partnerRequests.scss";
import { Navigation } from "../../components/Navigation/Navigation"; import { Navigation } from "../../components/Navigation/Navigation";