Fixed copmonents
This commit is contained in:
parent
08f7d13f01
commit
2ad78834f3
@ -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"),
|
||||||
};
|
};
|
||||||
|
@ -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()],
|
||||||
});
|
});
|
@ -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()],
|
||||||
});
|
});
|
@ -1,26 +1,25 @@
|
|||||||
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'],
|
react: ["react", "react-dom", "prop-types"],
|
||||||
helpers: ['immer', 'nanoid']
|
helpers: ["immer", "nanoid"],
|
||||||
},
|
},
|
||||||
devtool: false,
|
devtool: false,
|
||||||
output: {
|
output: {
|
||||||
filename: 'js/[name].[hash:8].bundle.js',
|
filename: "js/[name].[hash:8].bundle.js",
|
||||||
publicPath: '/',
|
publicPath: "/",
|
||||||
assetModuleFilename: '[hash][ext][query]'
|
assetModuleFilename: "[hash][ext][query]",
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
@ -29,34 +28,32 @@ module.exports = merge(common, {
|
|||||||
use: [
|
use: [
|
||||||
MiniCssExtractPlugin.loader,
|
MiniCssExtractPlugin.loader,
|
||||||
{
|
{
|
||||||
loader: 'css-loader',
|
loader: "css-loader",
|
||||||
options: {importLoaders: 1}
|
options: { importLoaders: 1 },
|
||||||
},
|
},
|
||||||
'postcss-loader',
|
"postcss-loader",
|
||||||
'sass-loader'
|
"sass-loader",
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(jpe?g|png|gif|svg)$/i,
|
test: /\.(jpe?g|png|gif|svg|webp)$/i,
|
||||||
type: 'asset/resource'
|
type: "asset/resource",
|
||||||
// type: 'asset'
|
// type: 'asset'
|
||||||
},
|
},
|
||||||
|
],
|
||||||
]
|
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
filename: '[name].[contenthash].css',
|
filename: "[name].[contenthash].css",
|
||||||
chunkFilename: '[id].css'
|
chunkFilename: "[id].css",
|
||||||
}),
|
}),
|
||||||
|
|
||||||
],
|
],
|
||||||
optimization: {
|
optimization: {
|
||||||
runtimeChunk: 'single'
|
runtimeChunk: "single",
|
||||||
},
|
},
|
||||||
performance: {
|
performance: {
|
||||||
hints: 'warning',
|
hints: "warning",
|
||||||
maxEntrypointSize: 512000,
|
maxEntrypointSize: 512000,
|
||||||
maxAssetSize: 512000
|
maxAssetSize: 512000,
|
||||||
}
|
},
|
||||||
});
|
});
|
@ -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>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
@ -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";
|
||||||
|
@ -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";
|
@ -1,8 +1,8 @@
|
|||||||
.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;
|
||||||
@ -56,18 +56,18 @@
|
|||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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";
|
||||||
|
|
@ -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>
|
|
||||||
)
|
|
||||||
}
|
|
53
src/components/Footer/Footer.jsx
Normal file
53
src/components/Footer/Footer.jsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
@ -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;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
@ -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
|
|
125
src/components/Form/Form.jsx
Normal file
125
src/components/Form/Form.jsx
Normal 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;
|
@ -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;
|
||||||
|
@ -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>
|
|
||||||
)
|
|
||||||
};
|
|
16
src/components/Header/Header.jsx
Normal file
16
src/components/Header/Header.jsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
@ -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;
|
||||||
|
@ -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>
|
|
||||||
)
|
|
||||||
};
|
|
17
src/components/Loader/Loader.jsx
Normal file
17
src/components/Loader/Loader.jsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
@ -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;
|
||||||
|
@ -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>
|
|
||||||
)
|
|
||||||
};
|
|
31
src/components/LogoutButton/LogoutButton.jsx
Normal file
31
src/components/LogoutButton/LogoutButton.jsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
@ -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 {
|
||||||
|
@ -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,
|
@ -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
|
|
126
src/components/OutstaffingBlock/OutstaffingBlock.jsx
Normal file
126
src/components/OutstaffingBlock/OutstaffingBlock.jsx
Normal 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;
|
@ -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;
|
||||||
|
@ -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>
|
|
||||||
)
|
|
||||||
}
|
|
18
src/components/ProfileBreadcrumbs/ProfileBreadcrumbs.jsx
Normal file
18
src/components/ProfileBreadcrumbs/ProfileBreadcrumbs.jsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
@ -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";
|
||||||
|
|
||||||
|
@ -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" },
|
||||||
|
{ name: "Страница добавления нового отчета", link: "/report" },
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
<h2 className='summary__title'>Ваши отчеты - <span>добавить отчет</span></h2>
|
<h2 className="summary__title">
|
||||||
|
Ваши отчеты - <span>добавить отчет</span>
|
||||||
|
</h2>
|
||||||
<div>
|
<div>
|
||||||
<div className='report__head'>
|
<div className="report__head">
|
||||||
<Link className='calendar__back' to={`/profile/calendar`}>
|
<Link className="calendar__back" to={`/profile/calendar`}>
|
||||||
<img src={arrow} alt=''/><p>Вернуться</p>
|
<img src={arrow} alt="" />
|
||||||
|
<p>Вернуться</p>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</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
|
||||||
|
className="report-form__block-img"
|
||||||
|
onClick={() => setDatePickerOpen(true)}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
className='report-form__calendar-icon'
|
className="report-form__calendar-icon"
|
||||||
src={calendarIcon}
|
src={calendarIcon}
|
||||||
alt=''
|
alt=""
|
||||||
/>
|
/>
|
||||||
{getCorrectDate(startDate)}
|
{getCorrectDate(startDate)}
|
||||||
</div>
|
</div>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
className='datePicker'
|
className="datePicker"
|
||||||
open={datePickerOpen}
|
open={datePickerOpen}
|
||||||
locale="ru"
|
locale="ru"
|
||||||
selected={startDate}
|
selected={startDate}
|
||||||
onChange={(date) => {
|
onChange={(date) => {
|
||||||
setDatePickerOpen(false)
|
setDatePickerOpen(false);
|
||||||
setStartDate(date)
|
setStartDate(date);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className='report-form__task-list'>
|
<div className="report-form__task-list">
|
||||||
<img src={ellipse} alt=''/>
|
<img src={ellipse} alt="" />
|
||||||
<span>Какие задачи были выполнены?</span>
|
<span>Какие задачи были выполнены?</span>
|
||||||
</div>
|
</div>
|
||||||
</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">
|
||||||
|
Количество часов
|
||||||
|
</p>
|
||||||
</div>
|
</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">
|
||||||
|
<input
|
||||||
|
value={inputs[index].task}
|
||||||
|
className={
|
||||||
|
!input.task && reportSuccess === "Заполните задачи"
|
||||||
|
? "checkTask"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
name="text"
|
||||||
|
type="text"
|
||||||
|
onChange={(e) =>
|
||||||
|
setInputs(
|
||||||
|
inputs.map((input, inputIndex) => {
|
||||||
return index === inputIndex
|
return index === inputIndex
|
||||||
? {
|
? {
|
||||||
...input,
|
...input,
|
||||||
task: e.target.value
|
task: e.target.value,
|
||||||
}
|
}
|
||||||
: input
|
: 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>
|
||||||
|
<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}>
|
||||||
|
+
|
||||||
|
</p>
|
||||||
<span>Добавить еще </span>
|
<span>Добавить еще </span>
|
||||||
</div>
|
</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__input-box">
|
||||||
<div className='report-form__troubles'>
|
<div className="report-form__troubles">
|
||||||
<img src={ellipse} alt=''/>
|
<img src={ellipse} alt="" />
|
||||||
<span>Какие сложности возникли?</span>
|
<span>Какие сложности возникли?</span>
|
||||||
</div>
|
</div>
|
||||||
<input type='text' value={troublesInputValue} onChange={e => setTroublesInputValue(e.target.value)}/>
|
<input
|
||||||
<div className='report-form__scheduled'>
|
type="text"
|
||||||
<img src={ellipse} alt=''/>
|
value={troublesInputValue}
|
||||||
|
onChange={(e) => setTroublesInputValue(e.target.value)}
|
||||||
|
/>
|
||||||
|
<div className="report-form__scheduled">
|
||||||
|
<img src={ellipse} alt="" />
|
||||||
<span>Что планируется сделать завтра?</span>
|
<span>Что планируется сделать завтра?</span>
|
||||||
</div>
|
</div>
|
||||||
<input type='text' value={scheduledInputValue} onChange={e => setScheduledInputValue(e.target.value)}/>
|
<input
|
||||||
|
type="text"
|
||||||
|
value={scheduledInputValue}
|
||||||
|
onChange={(e) => setScheduledInputValue(e.target.value)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='row'>
|
<div className="row">
|
||||||
<div className='col-12'>
|
<div className="col-12">
|
||||||
<div className='report-form__footer'>
|
<div className="report-form__footer">
|
||||||
<button className='report-form__footer-btn' onClick={() => handler()}>
|
<button
|
||||||
{isFetching ? <Loader/> : 'Отправить'}
|
className="report-form__footer-btn"
|
||||||
|
onClick={() => handler()}
|
||||||
|
>
|
||||||
|
{isFetching ? <Loader /> : "Отправить"}
|
||||||
</button>
|
</button>
|
||||||
<p className='report-form__footer-text'>
|
<p className="report-form__footer-text">
|
||||||
Всего за день : <span>{totalHours} {hourOfNum(totalHours)}</span>
|
Всего за день :{" "}
|
||||||
|
<span>
|
||||||
|
{totalHours} {hourOfNum(totalHours)}
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
{reportSuccess &&
|
{reportSuccess && (
|
||||||
<p className={`report-form__footer-done ${reportSuccess === 'Заполните задачи' ? 'errorText' : ''}`}>{reportSuccess}</p>
|
<p
|
||||||
}
|
className={`report-form__footer-done ${
|
||||||
|
reportSuccess === "Заполните задачи" ? "errorText" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{reportSuccess}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Footer/>
|
<Footer />
|
||||||
</section>
|
</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
3
src/images/cursorImg.svg
Normal 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 |
@ -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";
|
||||||
|
Loading…
Reference in New Issue
Block a user