@ -1,2 +1,2 @@
|
||||
REACT_APP_API_URL = https://dev.itguild.info
|
||||
REACT_APP_BASE_URL = https://dev.itguild.info
|
||||
REACT_APP_API_URL = https://dev.itguild.info/api
|
||||
REACT_APP_BASE_URL = https://dev.itguild.info/api
|
29159
package-lock.json
generated
@ -12,7 +12,7 @@
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"@typescript-eslint/eslint-plugin": "^4.5.0",
|
||||
"@typescript-eslint/parser": "^4.5.0",
|
||||
"axios": "^0.21.1",
|
||||
"axios": "^0.24.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-jest": "^26.6.0",
|
||||
"babel-loader": "8.1.0",
|
||||
@ -65,7 +65,7 @@
|
||||
"react-phone-input-2": "^2.14.0",
|
||||
"react-redux": "^7.2.4",
|
||||
"react-refresh": "^0.8.3",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-router-dom": "^6.2.1",
|
||||
"react-select": "^4.3.1",
|
||||
"react-syntax-highlighter": "^15.4.5",
|
||||
"react-yandex-metrika": "^2.6.0",
|
||||
@ -84,7 +84,8 @@
|
||||
"webpack": "4.44.2",
|
||||
"webpack-dev-server": "3.11.1",
|
||||
"webpack-manifest-plugin": "2.2.0",
|
||||
"workbox-webpack-plugin": "5.1.4"
|
||||
"workbox-webpack-plugin": "5.1.4",
|
||||
"react-router": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node scripts/start.js",
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" style="height: 100%">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
@ -14,7 +14,7 @@
|
||||
<link rel="shortcut icon" href="favicon.ico" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body style="min-height: 100%">
|
||||
<!-- <noscript>You need to enable JavaScript to run this app.</noscript> -->
|
||||
<div id="root"></div>
|
||||
<!-- <script type="text/javascript">
|
||||
|
88
src/App.js
@ -1,66 +1,66 @@
|
||||
import React from 'react'
|
||||
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
|
||||
import 'bootstrap/dist/css/bootstrap.min.css'
|
||||
import './fonts/stylesheet.css'
|
||||
import {BrowserRouter as Router, Route, Routes, Navigate} from 'react-router-dom';
|
||||
|
||||
import { ProtectedRoute } from './components/ProtectedRoute/ProtectedRoute'
|
||||
|
||||
import AuthPageForDevelopers from './pages/AuthPageForDevelopers'
|
||||
import AuthPageForPartners from './pages/AuthPageForPartners'
|
||||
import AuthForPartners from "./pages/AuthForPartners/AuthForPartners";
|
||||
import AuthForDevelopers from "./pages/AuthForDevelopers/AuthForDevelopers";
|
||||
import HomePage from './pages/HomePage'
|
||||
import CandidatePage from './pages/CandidatePage'
|
||||
import CalendarPage from './pages/CalendarPage'
|
||||
import ReportPage from './pages/ReportFormPage.js'
|
||||
import ProfileCalendarPage from './pages/ProfileCalendarPage.js'
|
||||
import FormPage from './pages/FormPage.js'
|
||||
import SingleReportPage from './pages/SingleReportPage'
|
||||
import { QuizPage } from './pages/quiz/QuizPage'
|
||||
import { InterjacentPage } from './pages/quiz/InterjacentPage'
|
||||
import { QuizTestPage } from './pages/quiz/QuizTestPage'
|
||||
import { InstructionPage } from './pages/quiz/InstructionPage'
|
||||
import { ResultPage } from './pages/quiz/ResultPage'
|
||||
import {QuizPage} from './pages/quiz/QuizPage'
|
||||
import {InterjacentPage} from './pages/quiz/InterjacentPage'
|
||||
import {QuizTestPage} from './pages/quiz/QuizTestPage'
|
||||
import {InstructionPage} from './pages/quiz/InstructionPage'
|
||||
import {ResultPage} from './pages/quiz/ResultPage'
|
||||
import {Profile} from './pages/Profile/Profile.js'
|
||||
import {Summary} from './pages/Summary/Summary'
|
||||
|
||||
import './fonts/stylesheet.css'
|
||||
import 'bootstrap/dist/css/bootstrap.min.css'
|
||||
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<>
|
||||
<h1>IT Аутстаффинг в России</h1>
|
||||
<Router>
|
||||
<Switch>
|
||||
<Route path='/authdev' exact>
|
||||
<AuthPageForDevelopers />
|
||||
|
||||
<Routes>
|
||||
<Route exact path='/' element={<HomePage/>}/>
|
||||
|
||||
<Route exact path='/authdev' element={<AuthForDevelopers/>}/>
|
||||
<Route exact path='/auth' element={<AuthForPartners/>}/>
|
||||
|
||||
<Route exact path='/candidate/:id' element={<CandidatePage/>}/>
|
||||
<Route exact path='/candidate/:id/form' element={<FormPage/>}/>
|
||||
<Route path='/:userId/calendar' element={<CalendarPage/>}/>
|
||||
|
||||
<Route exact path='/report' element={<ReportPage/>}/>
|
||||
<Route path='/report/:id' element={<SingleReportPage/>}/>
|
||||
|
||||
<Route exact path='quiz'>
|
||||
<Route index element={<QuizPage/>}/>
|
||||
<Route exact path='interjacent' element={<InterjacentPage/>}/>
|
||||
<Route exact path='test' element={<QuizTestPage/>}/>
|
||||
<Route exact path='instruction' element={<InstructionPage/>}/>
|
||||
<Route exact path='result' element={<ResultPage/>}/>
|
||||
</Route>
|
||||
<Route path='/auth' exact>
|
||||
<AuthPageForPartners />
|
||||
|
||||
<Route exact path='profile'>
|
||||
<Route index element={<Profile/>}/>
|
||||
<Route exact path='calendar' element={<ProfileCalendarPage/>}/>
|
||||
<Route exact path='summary' element={<Summary/>}/>
|
||||
</Route>
|
||||
<ProtectedRoute exact path='/' component={HomePage} />
|
||||
<ProtectedRoute
|
||||
exact
|
||||
path='/candidate/:id'
|
||||
component={CandidatePage}
|
||||
/>
|
||||
<ProtectedRoute path='/:userId/calendar' component={CalendarPage} />
|
||||
<ProtectedRoute
|
||||
exact
|
||||
path='/candidate/:id/form'
|
||||
component={FormPage}
|
||||
/>
|
||||
<ProtectedRoute exact path='/report' component={ReportPage} />
|
||||
<ProtectedRoute path='/report/:id' component={SingleReportPage} />
|
||||
<ProtectedRoute path='/quiz' component={QuizPage} />
|
||||
<ProtectedRoute
|
||||
path='/quiz-interjacent'
|
||||
component={InterjacentPage}
|
||||
/>
|
||||
<ProtectedRoute path='/quiz-test' component={QuizTestPage} />
|
||||
<ProtectedRoute
|
||||
path='/quiz-instruction'
|
||||
component={InstructionPage}
|
||||
/>
|
||||
<ProtectedRoute path='/quiz-result' component={ResultPage} />
|
||||
<ProtectedRoute component={() => <div>Page not found</div>} />
|
||||
</Switch>
|
||||
|
||||
<Route path="*" element={<Navigate to="/" replace/>}/>
|
||||
</Routes>
|
||||
</Router>
|
||||
</>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default App
|
||||
|
48
src/api/request.js
Normal file
@ -0,0 +1,48 @@
|
||||
// import axios from 'axios';
|
||||
// import {getToken, urlHasParams} from "../helper";
|
||||
//
|
||||
//
|
||||
//
|
||||
// const instance = axios.create({
|
||||
// baseURL: process.env.REACT_APP_API_URL,
|
||||
// validateStatus(status) {
|
||||
// return status;
|
||||
// },
|
||||
// });
|
||||
//
|
||||
// export default function request(url, {method = 'get', params, data, headers} = {}) {
|
||||
// const fullHeaders = {...headers, ...getToken()};
|
||||
// let urWithParams = urlHasParams(url);
|
||||
//
|
||||
//
|
||||
// return instance
|
||||
// .request({
|
||||
// url: urWithParams,
|
||||
// method,
|
||||
// params,
|
||||
// data,
|
||||
// headers: {...fullHeaders},
|
||||
// })
|
||||
// .then(response => new Promise(resolve => {
|
||||
// console.log(response, 1)
|
||||
// if(response.data.redirect || response.status === 401) {
|
||||
//
|
||||
// // window.location.href = "/auth"
|
||||
// }
|
||||
// return resolve(response)
|
||||
// }))
|
||||
// .then(response => new Promise(resolve => resolve(response.data)))
|
||||
// }
|
||||
//
|
||||
// function RequestError(code, msg, data) {
|
||||
// const description = msg ? `- ${msg}` : '';
|
||||
//
|
||||
// this.name = 'RequestError';
|
||||
// this.message = `API returned: ${code}${description}.`;
|
||||
// this.code = code;
|
||||
// this.description = msg;
|
||||
// this.data = data;
|
||||
// }
|
||||
//
|
||||
// RequestError.prototype = Object.create(Error.prototype);
|
||||
// RequestError.prototype.constructor = RequestError;
|
@ -6,7 +6,7 @@ export const Achievement = ({ achievement }) => {
|
||||
return (
|
||||
<div className='achievement'>
|
||||
<div className='achievement__icon'>
|
||||
<img src={achievement.img} />
|
||||
<img src={achievement.img} alt='achievement' />
|
||||
</div>
|
||||
<div className='achievement__popup'>
|
||||
<div className='achievement__title'>{achievement.title}</div>
|
||||
|
@ -1,31 +1,71 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import React, {useState} from 'react'
|
||||
import {Link} from 'react-router-dom'
|
||||
import {useDispatch, useSelector} from 'react-redux'
|
||||
import {withSwalInstance} from 'sweetalert2-react'
|
||||
import swal from 'sweetalert2'
|
||||
|
||||
import {Loader} from '../Loader/Loader'
|
||||
|
||||
import {auth, setUserInfo} from '../../redux/outstaffingSlice'
|
||||
import {loading} from '../../redux/loaderSlice'
|
||||
import {setRole} from '../../redux/roleSlice'
|
||||
|
||||
import {selectIsLoading} from '../../redux/loaderSlice'
|
||||
|
||||
import { auth, setUserInfo } from '../../redux/outstaffingSlice'
|
||||
import { loading } from '../../redux/loaderSlice'
|
||||
import ellipse from '../../images/ellipse.png'
|
||||
|
||||
import { Loader } from '../Loader/Loader'
|
||||
|
||||
import { fetchAuth } from '../../server/server'
|
||||
import { setRole } from '../../redux/roleSlice'
|
||||
import { selectIsLoading } from '../../redux/loaderSlice'
|
||||
|
||||
import './authBox.scss'
|
||||
import {useRequest} from "../../hooks/useRequest";
|
||||
|
||||
import { withSwalInstance } from 'sweetalert2-react'
|
||||
import swal from 'sweetalert2'
|
||||
const SweetAlert = withSwalInstance(swal)
|
||||
|
||||
export const AuthBox = ({ title, altTitle, roleChangeLink }) => {
|
||||
const dispatch = useDispatch()
|
||||
const SweetAlert = withSwalInstance(swal);
|
||||
|
||||
const isLoading = useSelector(selectIsLoading)
|
||||
export const AuthBox = ({title, altTitle, roleChangeLink}) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [username, setUsername] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const [error, setError] = useState(null)
|
||||
const {apiRequest} = useRequest();
|
||||
|
||||
const isLoading = useSelector(selectIsLoading);
|
||||
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
const submitHandler = () => {
|
||||
|
||||
if (!isLoading) {
|
||||
dispatch(loading(true));
|
||||
apiRequest('/user/login',
|
||||
{
|
||||
method: 'POST',
|
||||
data: JSON.stringify({
|
||||
username,
|
||||
password
|
||||
})
|
||||
}).then((res) => {
|
||||
|
||||
if (!res.access_token) {
|
||||
|
||||
setError('Некорректные данные для входа');
|
||||
dispatch(loading(false))
|
||||
|
||||
} else {
|
||||
|
||||
localStorage.setItem('auth_token', res.access_token);
|
||||
localStorage.setItem('id', res.id);
|
||||
localStorage.setItem('cardId', res.card_id);
|
||||
localStorage.setItem(
|
||||
'access_token_expired_at',
|
||||
res.access_token_expired_at
|
||||
);
|
||||
dispatch(auth(true));
|
||||
dispatch(setUserInfo(res));
|
||||
dispatch(loading(false));
|
||||
dispatch(setRole('ROLE_PARTNER'))
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='auth-box'>
|
||||
@ -33,7 +73,7 @@ export const AuthBox = ({ title, altTitle, roleChangeLink }) => {
|
||||
Войти в <span>систему</span>
|
||||
</h2>
|
||||
<div className='auth-box__title'>
|
||||
<img src={ellipse} alt='' />
|
||||
<img src={ellipse} alt=''/>
|
||||
<span>{title}</span>
|
||||
</div>
|
||||
<form className='auth-box__form'>
|
||||
@ -69,30 +109,12 @@ export const AuthBox = ({ title, altTitle, roleChangeLink }) => {
|
||||
<div className='auth-box__form-buttons'>
|
||||
<button
|
||||
className='auth-box__form-btn'
|
||||
onClick={
|
||||
!isLoading
|
||||
? (e) => {
|
||||
e.preventDefault()
|
||||
dispatch(loading(true))
|
||||
fetchAuth({
|
||||
username,
|
||||
password,
|
||||
dispatch: (resJSON) => {
|
||||
dispatch(auth(true))
|
||||
dispatch(setUserInfo(resJSON))
|
||||
dispatch(loading(false))
|
||||
dispatch(setRole('ROLE_PARTNER'))
|
||||
},
|
||||
catchError: () => {
|
||||
setError('Некорректные данные для входа')
|
||||
dispatch(loading(false))
|
||||
}
|
||||
})
|
||||
}
|
||||
: () => {}
|
||||
}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
submitHandler()
|
||||
}}
|
||||
>
|
||||
{isLoading ? <Loader /> : 'Войти'}
|
||||
{isLoading ? <Loader/> : 'Войти'}
|
||||
</button>
|
||||
|
||||
<Link to={roleChangeLink}>
|
||||
@ -104,4 +126,4 @@ export const AuthBox = ({ title, altTitle, roleChangeLink }) => {
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
@ -1,57 +1,29 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { selectCurrentCandidate, auth } from '../../redux/outstaffingSlice'
|
||||
import { Link, useHistory, useParams } from 'react-router-dom'
|
||||
import calendarMale from '../../images/medium_male.png'
|
||||
import rectangle from '../../images/rectangle_secondPage.png'
|
||||
import CalendarComponent from './CalendarComponent'
|
||||
import { currentMonth } from './calendarHelper'
|
||||
import { Footer } from '../Footer/Footer'
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {useSelector} from 'react-redux'
|
||||
import {selectCurrentCandidate} from '../../redux/outstaffingSlice'
|
||||
import {Link} from 'react-router-dom'
|
||||
|
||||
import { fetchReportList } from '../../server/server'
|
||||
import { getRole } from '../../redux/roleSlice'
|
||||
import CalendarComponent from './CalendarComponent'
|
||||
import {currentMonth} from './calendarHelper'
|
||||
import {Footer} from '../Footer/Footer'
|
||||
|
||||
import rectangle from '../../images/rectangle_secondPage.png'
|
||||
|
||||
import './calendar.scss'
|
||||
|
||||
const getDateParamString = ({ paramName, value }) => {
|
||||
return value ? `${paramName}=${value}` : ''
|
||||
}
|
||||
const Calendar = ({onSelect}) => {
|
||||
|
||||
const Calendar = ({ onSelect }) => {
|
||||
const dispatch = useDispatch()
|
||||
const candidateForCalendar = useSelector(selectCurrentCandidate)
|
||||
const role = useSelector(getRole)
|
||||
const { userId } = useParams()
|
||||
const [month, setMonth] = useState('')
|
||||
const [fromDate, setFromDate] = useState(null)
|
||||
const [toDate, setToDate] = useState(null)
|
||||
const candidateForCalendar = useSelector(selectCurrentCandidate);
|
||||
|
||||
const history = useHistory()
|
||||
|
||||
// useEffect(() => {
|
||||
// fetchReportList({
|
||||
// link: `${
|
||||
// process.env.REACT_APP_API_URL
|
||||
// }/api/reports/index?user_id=${userId}${getDateParamString({
|
||||
// paramName: 'fromDate',
|
||||
// value: fromDate
|
||||
// })}${getDateParamString({
|
||||
// paramName: 'toDate',
|
||||
// value: toDate
|
||||
// })}`,
|
||||
// history,
|
||||
// role,
|
||||
// logout: () => {}
|
||||
// })
|
||||
// }, [])
|
||||
const [month, setMonth] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
setMonth(currentMonth)
|
||||
}, [month])
|
||||
}, [month]);
|
||||
|
||||
const { name, skillsName, photo } = candidateForCalendar
|
||||
const {name, skillsName, photo} = candidateForCalendar;
|
||||
|
||||
const abbreviatedName = name && name.substring(0, name.lastIndexOf(' '))
|
||||
const abbreviatedName = name && name.substring(0, name.lastIndexOf(' '));
|
||||
|
||||
return (
|
||||
<section className='calendar'>
|
||||
@ -61,12 +33,12 @@ const Calendar = ({ onSelect }) => {
|
||||
</h2>
|
||||
<div className='col-12 col-xl-12 d-flex justify-content-between align-items-center flex-column flex-sm-row'>
|
||||
<div className='calendar__info'>
|
||||
<img className='calendar__info-img' src={photo} alt='img' />
|
||||
<img className='calendar__info-img' src={photo} alt='img'/>
|
||||
<h3 className='calendar__info-name'>{abbreviatedName}</h3>
|
||||
</div>
|
||||
<div className='calendar__title'>
|
||||
<h3 className='calendar__title-text'>{skillsName} разработчик</h3>
|
||||
<img className='calendar__title-img' src={rectangle} alt='img' />
|
||||
<img className='calendar__title-img' src={rectangle} alt='img'/>
|
||||
</div>
|
||||
<div>
|
||||
<Link to='/report'>
|
||||
@ -78,15 +50,15 @@ const Calendar = ({ onSelect }) => {
|
||||
|
||||
<div className='row'>
|
||||
<div className='col-12 col-xl-12'>
|
||||
<CalendarComponent onSelect={onSelect} />
|
||||
<CalendarComponent onSelect={onSelect}/>
|
||||
<p className='calendar__hours'>
|
||||
{month} : <span> 60 часов </span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
<Footer/>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default Calendar
|
||||
|
@ -1,9 +1,9 @@
|
||||
.calendar {
|
||||
margin-bottom: 40px;
|
||||
font-family: 'LabGrotesque', sans-serif;
|
||||
|
||||
&__profile {
|
||||
color: #282828;
|
||||
font-family: 'GT Eesti Pro Display';
|
||||
font-size: 3.4em;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
@ -29,7 +29,6 @@
|
||||
height: 112px;
|
||||
|
||||
&-name {
|
||||
font-family: 'GT Eesti Pro Display';
|
||||
font-size: 2em;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
@ -53,7 +52,6 @@
|
||||
|
||||
&__title {
|
||||
&-text {
|
||||
font-family: 'GT Eesti Pro Display';
|
||||
font-size: 2.7em;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
@ -66,7 +64,6 @@
|
||||
&__btn {
|
||||
width: 280px;
|
||||
height: 62px;
|
||||
box-shadow: 6px 5px 20px rgba(82, 151, 34, 0.21);
|
||||
border-radius: 31px;
|
||||
background-color: #ffffff;
|
||||
background-image: linear-gradient(to top, #6aaf5c 0%, #52b709 100%),
|
||||
@ -83,10 +80,15 @@
|
||||
font-size: 1.6em;
|
||||
letter-spacing: normal;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 6px 5px 20px rgb(87 98 80 / 21%);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
}
|
||||
|
||||
&__hours {
|
||||
font-family: 'GT Eesti Pro Display';
|
||||
font-size: 2.2em;
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
@ -96,7 +98,6 @@
|
||||
margin-left: 68px;
|
||||
|
||||
span {
|
||||
font-family: 'GT Eesti Pro Display';
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
letter-spacing: normal;
|
||||
|
@ -1,18 +1,19 @@
|
||||
.calendar-component {
|
||||
position: relative;
|
||||
margin-top: 80px;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 60px;
|
||||
background-color: #f9f9f9;
|
||||
padding-left: 68px;
|
||||
padding-right: 54px;
|
||||
padding-top: 48px;
|
||||
padding-bottom: 94px;
|
||||
font-family: 'LabGrotesque', sans-serif;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
|
||||
h3 {
|
||||
font-family: 'GT Eesti Pro Display';
|
||||
|
||||
font-size: 2.5em;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
@ -33,7 +34,6 @@
|
||||
|
||||
span {
|
||||
color: #18586e;
|
||||
font-family: 'GT Eesti Pro Display';
|
||||
font-size: 1.6em;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
@ -62,7 +62,6 @@
|
||||
|
||||
p {
|
||||
color: #398208;
|
||||
font-family: 'GT Eesti Pro Display';
|
||||
font-size: 25px;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
@ -87,13 +86,17 @@
|
||||
border: 1px solid #c4c4c4;
|
||||
background-color: #ffffff;
|
||||
margin-top: 20px;
|
||||
font-family: 'GT Eesti Pro Display';
|
||||
font-size: 1.2em;
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
letter-spacing: normal;
|
||||
line-height: normal;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -250,7 +253,11 @@
|
||||
}
|
||||
|
||||
.before {
|
||||
background-color: #f0f7e0 !important;
|
||||
background-color: #e5f9b6 !important;
|
||||
}
|
||||
|
||||
.pass {
|
||||
background-color: #f7d7c9 !important;
|
||||
}
|
||||
|
||||
.today {
|
||||
@ -261,3 +268,7 @@
|
||||
.selected {
|
||||
background-color: #f9f9c3 !important;
|
||||
}
|
||||
|
||||
.block {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
@ -20,6 +20,16 @@ export function calendarHelper(value) {
|
||||
return calendar;
|
||||
}
|
||||
|
||||
export function getReports(value) {
|
||||
const startDay = value.clone().startOf('month').startOf('week').startOf('day');
|
||||
const reportsStart = `${new Date(startDay).getFullYear()}-${new Date(startDay).getMonth() + 1}-${new Date(startDay).getDate()}`
|
||||
const endDay = value.clone().endOf('month').endOf('week');
|
||||
const reportsEnd = `${new Date(endDay).getFullYear()}-${new Date(endDay).getMonth() + 1}-${new Date(endDay).getDate()}`
|
||||
const getReports = `fromDate=${reportsStart}&toDate=${reportsEnd}`
|
||||
|
||||
return getReports;
|
||||
}
|
||||
|
||||
export function currentMonth() {
|
||||
const currentMonth = moment().format('MMMM');
|
||||
|
||||
|
@ -1,43 +1,41 @@
|
||||
import React, {useEffect} from 'react'
|
||||
import {useHistory, useParams, Link} from 'react-router-dom'
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {useParams, Link, useNavigate} from 'react-router-dom'
|
||||
import {useSelector, useDispatch} from 'react-redux'
|
||||
import {
|
||||
currentCandidate,
|
||||
selectCurrentCandidate,
|
||||
auth
|
||||
} from '../../redux/outstaffingSlice'
|
||||
|
||||
import SkillSection from '../SkillSection/SkillSection'
|
||||
import Sidebar from '../CandidateSidebar/CandidateSidebar'
|
||||
import {Footer} from '../Footer/Footer'
|
||||
|
||||
import {currentCandidate, selectCurrentCandidate,} from '../../redux/outstaffingSlice'
|
||||
|
||||
import {useRequest} from "../../hooks/useRequest";
|
||||
import {createMarkup} from "../../helper";
|
||||
|
||||
import arrow from '../../images/right-arrow.png'
|
||||
import rectangle from '../../images/rectangle_secondPage.png'
|
||||
import Sidebar from '../CandidateSidebar/CandidateSidebar'
|
||||
import SkillSection from '../SkillSection/SkillSection'
|
||||
import front from '../../images/front_end.png'
|
||||
import back from '../../images/back_end.png'
|
||||
import design from '../../images/design.png'
|
||||
import {fetchGet} from '../../server/server'
|
||||
import {Footer} from '../Footer/Footer'
|
||||
|
||||
import './candidate.scss'
|
||||
import {getRole} from '../../redux/roleSlice'
|
||||
import {useState} from 'react'
|
||||
|
||||
|
||||
const Candidate = () => {
|
||||
const history = useHistory();
|
||||
const {id: candidateId} = useParams();
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const role = useSelector(getRole);
|
||||
|
||||
const [activeSnippet, setActiveSnippet] = useState(true);
|
||||
|
||||
const {apiRequest} = useRequest();
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0)
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/profile/${candidateId}`,
|
||||
apiRequest(`/profile/${candidateId}`, {
|
||||
params: Number(candidateId),
|
||||
history,
|
||||
role,
|
||||
logout: () => dispatch(auth(false))
|
||||
}).then((el) => dispatch(currentCandidate(el)))
|
||||
}, [dispatch, candidateId]);
|
||||
|
||||
@ -79,10 +77,6 @@ const Candidate = () => {
|
||||
return styles
|
||||
};
|
||||
|
||||
function createMarkup(text) {
|
||||
return {__html: text.split('</p>').join('</p>')}
|
||||
}
|
||||
|
||||
const {header, img, classes} = setStyles();
|
||||
|
||||
return (
|
||||
@ -100,7 +94,7 @@ const Candidate = () => {
|
||||
<div className='row'>
|
||||
<div className='col-12 candidate__header'>
|
||||
|
||||
<div className='candidate__arrow' onClick={() => history.push('/')}>
|
||||
<div className='candidate__arrow' onClick={() => navigate('/')}>
|
||||
<div className='candidate__arrow-img'>
|
||||
<img src={arrow} alt=''/>
|
||||
</div>
|
||||
@ -141,20 +135,13 @@ const Candidate = () => {
|
||||
: 'Описание отсутствует...'}
|
||||
</p>
|
||||
)}
|
||||
{/* <Link to={`/candidate/${currentCandidateObj.id}/form`}>
|
||||
<button type="submit" className='candidate__btn'>
|
||||
Выбрать к собеседованию
|
||||
</button>
|
||||
</Link> */}
|
||||
|
||||
<SkillSection skillsArr={skillValues}/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
) :
|
||||
(
|
||||
// <div className="col-12 col-xl-8">
|
||||
// <CodeSnippetlighter />
|
||||
// </div>
|
||||
<div className="col-12 col-xl-8">
|
||||
<div className="candidate__works works">
|
||||
<div className="works__body">
|
||||
|
@ -2,7 +2,7 @@ import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Achievement } from '../Achievement/Achievement'
|
||||
|
||||
import { LEVELS, SKILLS } from '../constants/constants'
|
||||
import { LEVELS, SKILLS } from '../../constants/constants'
|
||||
|
||||
import './candidateSidebar.scss'
|
||||
|
||||
|
@ -1,43 +1,21 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import React from 'react'
|
||||
import {useSelector} from 'react-redux'
|
||||
import {Link} from 'react-router-dom'
|
||||
|
||||
import {Loader} from '../Loader/Loader'
|
||||
|
||||
import {LEVELS, SKILLS} from '../../constants/constants'
|
||||
import {selectProfiles, selectFilteredCandidates,} from '../../redux/outstaffingSlice'
|
||||
|
||||
import male from '../../images/medium_male.png'
|
||||
import rectangle from '../../images/rectangle_secondPage.png'
|
||||
import { Link, useHistory } from 'react-router-dom'
|
||||
import { LEVELS, SKILLS } from '../constants/constants'
|
||||
import {
|
||||
selectProfiles,
|
||||
selectFilteredCandidates,
|
||||
selectItems,
|
||||
auth
|
||||
} from '../../redux/outstaffingSlice'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import { fetchGet } from '../../server/server'
|
||||
import { Loader } from '../Loader/Loader'
|
||||
import { getRole } from '../../redux/roleSlice'
|
||||
|
||||
import './description.scss'
|
||||
|
||||
const Description = ({ onLoadMore, isLoadingMore }) => {
|
||||
const dispatch = useDispatch()
|
||||
const [isLoaded, setIsLoaded] = useState(false)
|
||||
const history = useHistory()
|
||||
const role = useSelector(getRole)
|
||||
const candidatesListArr = useSelector(selectProfiles)
|
||||
const itemsArr = useSelector(selectItems)
|
||||
const filteredListArr = useSelector(selectFilteredCandidates)
|
||||
const [allCandidates, getAllCandidates] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/profile?limit=`,
|
||||
params: 1000,
|
||||
history,
|
||||
role,
|
||||
logout: () => dispatch(auth(false))
|
||||
}).then((p) => {
|
||||
getAllCandidates(p)
|
||||
setIsLoaded(true)
|
||||
})
|
||||
}, [])
|
||||
const candidatesListArr = useSelector(selectProfiles);
|
||||
const filteredListArr = useSelector(selectFilteredCandidates);
|
||||
|
||||
if (!filteredListArr) {
|
||||
return (
|
||||
@ -95,7 +73,7 @@ const Description = ({ onLoadMore, isLoadingMore }) => {
|
||||
))
|
||||
) : (
|
||||
<div className='description__empty'>
|
||||
{isLoaded
|
||||
{isLoadingMore
|
||||
? 'В данный момент в категории нет свободных специалистов'
|
||||
: 'Загрузка...'}
|
||||
</div>
|
||||
@ -229,9 +207,8 @@ const Description = ({ onLoadMore, isLoadingMore }) => {
|
||||
<div className='col-12'>
|
||||
<div className='description__footer'>
|
||||
<div className='description__footer-btn'>
|
||||
{allCandidates &&
|
||||
{
|
||||
candidatesListArr &&
|
||||
candidatesListArr.length !== allCandidates.length &&
|
||||
filteredListArr.length === 0 ? (
|
||||
<button onClick={() => onLoadMore(2)}>Загрузить еще</button>
|
||||
) : null}
|
||||
@ -242,6 +219,6 @@ const Description = ({ onLoadMore, isLoadingMore }) => {
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default Description
|
||||
|
@ -1,7 +1,5 @@
|
||||
import React, { useState } from 'react'
|
||||
import { fetchPost } from '../../server/server'
|
||||
import { auth } from '../../redux/outstaffingSlice'
|
||||
import { useHistory, useParams, Redirect } from 'react-router-dom'
|
||||
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'
|
||||
@ -9,59 +7,57 @@ import './form.scss'
|
||||
|
||||
import { withSwalInstance } from 'sweetalert2-react'
|
||||
import swal from 'sweetalert2'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import { getRole } from '../../redux/roleSlice'
|
||||
import {useRequest} from "../../hooks/useRequest";
|
||||
|
||||
const SweetAlert = withSwalInstance(swal)
|
||||
|
||||
const SweetAlert = withSwalInstance(swal);
|
||||
|
||||
const Form = () => {
|
||||
const dispatch = useDispatch()
|
||||
const history = useHistory()
|
||||
const role = useSelector(getRole)
|
||||
const urlParams = useParams()
|
||||
const [status, setStatus] = useState(null)
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const urlParams = useParams();
|
||||
const [status, setStatus] = useState(null);
|
||||
const [data, setData] = useState({
|
||||
email: '',
|
||||
phone: '',
|
||||
comment: ''
|
||||
})
|
||||
const [isFetching, setIsFetching] = useState(false)
|
||||
});
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
|
||||
const {apiRequest} = useRequest();
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { id, value } = e.target
|
||||
const { id, value } = e.target;
|
||||
|
||||
setData((prev) => ({
|
||||
...prev,
|
||||
[id]: value
|
||||
}))
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault()
|
||||
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)
|
||||
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);
|
||||
|
||||
fetchPost({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/interview-request/create-interview-request`,
|
||||
apiRequest('/interview-request/create-interview-request',{
|
||||
method: 'POST',
|
||||
params: {
|
||||
profile_id: urlParams.id,
|
||||
...data
|
||||
},
|
||||
history,
|
||||
role,
|
||||
logout: () => dispatch(auth(false))
|
||||
}).then((res) =>
|
||||
res.json().then((resJSON) => {
|
||||
setStatus(resJSON)
|
||||
setIsFetching(false)
|
||||
})
|
||||
)
|
||||
}
|
||||
}).then((res) => {
|
||||
setStatus(res);
|
||||
setIsFetching(false)
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -79,8 +75,8 @@ const Form = () => {
|
||||
setStatus(null)
|
||||
}
|
||||
: () => {
|
||||
setStatus(null)
|
||||
history.push(`/candidate/${urlParams.id}`)
|
||||
setStatus(null);
|
||||
navigate(`/candidate/${urlParams.id}`)
|
||||
}
|
||||
}
|
||||
/>
|
||||
@ -135,6 +131,6 @@ const Form = () => {
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default Form
|
||||
|
@ -1,66 +1,64 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import React, {useState, useEffect} from 'react'
|
||||
import {useDispatch} from 'react-redux'
|
||||
|
||||
import Outstaffing from '../Outstaffing/Outstaffing'
|
||||
import Description from '../Description/Description'
|
||||
import { fetchGet } from '../../server/server'
|
||||
import { profiles, tags, auth } from '../../redux/outstaffingSlice'
|
||||
import { getRole } from '../../redux/roleSlice'
|
||||
import { Footer } from '../Footer/Footer'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import {Footer} from '../Footer/Footer'
|
||||
|
||||
import {profiles, tags} from '../../redux/outstaffingSlice'
|
||||
|
||||
import {useRequest} from "../../hooks/useRequest";
|
||||
|
||||
|
||||
const Home = () => {
|
||||
const history = useHistory()
|
||||
const [isLoadingMore, setIsLoadingMore] = useState(false)
|
||||
const [index, setIndex] = useState(4)
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const role = useSelector(getRole)
|
||||
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
||||
const [index, setIndex] = useState(4);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {apiRequest} = useRequest();
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoadingMore(true)
|
||||
fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/profile?limit=`,
|
||||
params: index,
|
||||
history,
|
||||
role,
|
||||
logout: () => dispatch(auth(false))
|
||||
setIsLoadingMore(true);
|
||||
apiRequest('/profile', {
|
||||
params: {limit: 1000},
|
||||
}).then((profileArr) => {
|
||||
dispatch(profiles(profileArr))
|
||||
setIsLoadingMore(false)
|
||||
})
|
||||
|
||||
fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/skills/skills-on-main-page`,
|
||||
history,
|
||||
role,
|
||||
logout: () => dispatch(auth(false))
|
||||
dispatch(profiles(profileArr));
|
||||
setIsLoadingMore(false)
|
||||
});
|
||||
|
||||
apiRequest('/skills/skills-on-main-page', {
|
||||
}).then((skills) => {
|
||||
if (!skills) {
|
||||
return []
|
||||
}
|
||||
const keys = Object.keys(skills)
|
||||
const values = Object.values(skills)
|
||||
|
||||
const keys = Object.keys(skills);
|
||||
const values = Object.values(skills);
|
||||
|
||||
const tempTags = values.map((value, index) =>
|
||||
value.map((val) => {
|
||||
return { id: val.id, value: val.tags, name: keys[index] }
|
||||
return {id: val.id, value: val.tags, name: keys[index]}
|
||||
})
|
||||
)
|
||||
);
|
||||
dispatch(tags(tempTags))
|
||||
})
|
||||
}, [dispatch, index])
|
||||
|
||||
}, [index]);
|
||||
|
||||
const loadMore = (count) => {
|
||||
setIndex((prev) => prev + count)
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Outstaffing />
|
||||
<Description onLoadMore={loadMore} isLoadingMore={isLoadingMore} />
|
||||
<Footer />
|
||||
<Outstaffing/>
|
||||
<Description onLoadMore={loadMore} isLoadingMore={isLoadingMore}/>
|
||||
<Footer/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default Home
|
||||
|
@ -1,5 +1,6 @@
|
||||
import SVGLoader from 'react-loader-spinner'
|
||||
import './loader.scss'
|
||||
import React from "react";
|
||||
|
||||
export const Loader = ({ width = 50, height = 50 }) => {
|
||||
return (
|
||||
@ -7,4 +8,4 @@ export const Loader = ({ width = 50, height = 50 }) => {
|
||||
<SVGLoader type='Circles' color='#fff' height={height} width={width} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
@ -4,7 +4,7 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
position: relative;
|
||||
&:hover {
|
||||
path {
|
||||
fill: #6aaf5c;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, {useState} from 'react'
|
||||
import {useHistory} from 'react-router-dom'
|
||||
import {useNavigate} from 'react-router-dom'
|
||||
import {useDispatch, useSelector} from 'react-redux'
|
||||
import {Loader} from '../Loader/Loader'
|
||||
import {auth} from '../../redux/outstaffingSlice'
|
||||
@ -11,7 +11,7 @@ export const LogoutButton = () => {
|
||||
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const userRole = useSelector(getRole);
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className='logout-button'>
|
||||
@ -21,7 +21,7 @@ export const LogoutButton = () => {
|
||||
localStorage.clear();
|
||||
dispatch(auth(false));
|
||||
setIsLoggingOut(false);
|
||||
history.push(userRole === 'ROLE_DEV' ? '/authdev' : '/auth')
|
||||
navigate(userRole === 'ROLE_DEV' ? '/authdev' : '/auth')
|
||||
}}
|
||||
>
|
||||
{isLoggingOut ? <Loader/> : 'Выйти'}{' '}
|
||||
|
@ -1,12 +1,11 @@
|
||||
import React, { useState } from 'react'
|
||||
import React from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
|
||||
import OutstaffingBlock from '../OutstaffingBlock/OutstaffingBlock'
|
||||
import TagSelect from '../Select/TagSelect'
|
||||
import {
|
||||
selectTags,
|
||||
getPositionId,
|
||||
setPositionId
|
||||
} from '../../redux/outstaffingSlice'
|
||||
|
||||
import { selectTags, getPositionId, setPositionId} from '../../redux/outstaffingSlice'
|
||||
|
||||
import front from '../../images/front_end.png'
|
||||
import back from '../../images/back_end.png'
|
||||
import design from '../../images/design.png'
|
||||
@ -21,18 +20,18 @@ const createSelectPositionHandler =
|
||||
} else {
|
||||
dispatch(setPositionId(id))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Outstaffing = () => {
|
||||
const dispatch = useDispatch()
|
||||
const positionId = useSelector(getPositionId)
|
||||
const tagsArr = useSelector(selectTags)
|
||||
const dispatch = useDispatch();
|
||||
const positionId = useSelector(getPositionId);
|
||||
const tagsArr = useSelector(selectTags);
|
||||
|
||||
const onSelectPosition = createSelectPositionHandler({
|
||||
positionId,
|
||||
setPositionId,
|
||||
dispatch
|
||||
})
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<section className='outstaffing'>
|
||||
@ -91,6 +90,6 @@ const Outstaffing = () => {
|
||||
<TagSelect />
|
||||
</>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default Outstaffing
|
||||
|
@ -1,55 +1,48 @@
|
||||
import React from 'react'
|
||||
import OutsideClickHandler from 'react-outside-click-handler'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import {useDispatch, useSelector} from 'react-redux'
|
||||
import {
|
||||
selectItems,
|
||||
selectedItems,
|
||||
filteredCandidates,
|
||||
auth
|
||||
} from '../../redux/outstaffingSlice'
|
||||
|
||||
import { fetchGet } from '../../server/server'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { getRole } from '../../redux/roleSlice'
|
||||
import {useRequest} from "../../hooks/useRequest";
|
||||
|
||||
import './outstaffingBlock.scss'
|
||||
|
||||
const handlePositionClick = ({
|
||||
|
||||
const handlePositionClick = (
|
||||
{
|
||||
dispatch,
|
||||
positionId,
|
||||
isSelected,
|
||||
onSelect,
|
||||
history,
|
||||
role
|
||||
}) => {
|
||||
apiRequest
|
||||
}) => {
|
||||
if (isSelected) {
|
||||
fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/profile?limit=`,
|
||||
params: 4,
|
||||
history,
|
||||
role,
|
||||
logout: () => dispatch(auth(false))
|
||||
apiRequest('/profile', {
|
||||
params: {limit: 1000},
|
||||
}).then((profileArr) => {
|
||||
dispatch(filteredCandidates(profileArr))
|
||||
dispatch(selectedItems([]))
|
||||
dispatch(filteredCandidates(profileArr));
|
||||
dispatch(selectedItems([]));
|
||||
onSelect(positionId)
|
||||
})
|
||||
} else {
|
||||
fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/profile?position_id=`,
|
||||
params: positionId,
|
||||
history,
|
||||
role,
|
||||
logout: () => dispatch(auth(false))
|
||||
apiRequest('/profile', {
|
||||
params: {
|
||||
limit: '1000',
|
||||
position_id: positionId},
|
||||
}).then((el) => {
|
||||
dispatch(filteredCandidates(el))
|
||||
dispatch(selectedItems([]))
|
||||
dispatch(filteredCandidates(el));
|
||||
dispatch(selectedItems([]));
|
||||
onSelect(positionId)
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const OutstaffingBlock = ({
|
||||
const OutstaffingBlock = (
|
||||
{
|
||||
dataTags = [],
|
||||
selected,
|
||||
img,
|
||||
@ -57,21 +50,22 @@ const OutstaffingBlock = ({
|
||||
positionId,
|
||||
isSelected,
|
||||
onSelect
|
||||
}) => {
|
||||
const history = useHistory()
|
||||
const role = useSelector(getRole)
|
||||
}) => {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const itemsArr = useSelector(selectItems)
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const itemsArr = useSelector(selectItems);
|
||||
|
||||
const {apiRequest} = useRequest();
|
||||
|
||||
const handleBlockClick = (item, id) => {
|
||||
if (!itemsArr.find((el) => item === el.value)) {
|
||||
dispatch(selectedItems([...itemsArr, { id, value: item, label: item }]))
|
||||
}
|
||||
dispatch(selectedItems([...itemsArr, {id, value: item, label: item}]))
|
||||
}
|
||||
};
|
||||
|
||||
let classes
|
||||
let classes;
|
||||
|
||||
dataTags.forEach((el) => {
|
||||
if (el.name === 'skills_back') {
|
||||
@ -81,7 +75,7 @@ const OutstaffingBlock = ({
|
||||
} else if (el.name === 'skills_front') {
|
||||
classes = 'front'
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return (
|
||||
<OutsideClickHandler
|
||||
@ -104,13 +98,12 @@ const OutstaffingBlock = ({
|
||||
positionId,
|
||||
isSelected,
|
||||
onSelect,
|
||||
history,
|
||||
role
|
||||
apiRequest
|
||||
})
|
||||
}
|
||||
>
|
||||
<h3>{header}</h3>
|
||||
<img className={classes} src={img} alt='img' />
|
||||
<img className={classes} src={img} alt='img'/>
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
@ -136,6 +129,6 @@ const OutstaffingBlock = ({
|
||||
</div>
|
||||
</OutsideClickHandler>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default OutstaffingBlock
|
||||
|
@ -3,6 +3,7 @@
|
||||
color: #f9f9f9;
|
||||
}
|
||||
.outstaffing-block {
|
||||
|
||||
margin-top: 60px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -28,6 +29,7 @@
|
||||
}
|
||||
|
||||
&__img {
|
||||
cursor: pointer;
|
||||
min-width: 260px;
|
||||
min-height: 120px;
|
||||
background-color: #f9f9f9;
|
||||
|
81
src/components/ProfileCalendar/ProfileCalendar.js
Normal file
@ -0,0 +1,81 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import {useDispatch, useSelector} from 'react-redux'
|
||||
|
||||
import { Link } from 'react-router-dom'
|
||||
import moment from "moment";
|
||||
|
||||
import {currentMonth, getReports} from '../Calendar/calendarHelper'
|
||||
import {ProfileCalendarComponent} from "./ProfileCalendarComponent";
|
||||
import { Footer } from '../Footer/Footer'
|
||||
import {ProfileHeader} from "../ProfileHeader/ProfileHeader";
|
||||
|
||||
import {useRequest} from "../../hooks/useRequest";
|
||||
import { getProfileInfo } from '../../redux/outstaffingSlice'
|
||||
import {setReportDate} from "../../redux/reportSlice";
|
||||
|
||||
import './profileCalendar.scss'
|
||||
|
||||
export const ProfileCalendar = () => {
|
||||
const dispatch = useDispatch();
|
||||
const profileInfo = useSelector(getProfileInfo);
|
||||
const [month, setMonth] = useState('');
|
||||
const [reports, setReports] = useState([]);
|
||||
const [totalHours, setTotalHours] = useState(0);
|
||||
const [requestDates, setRequestDates] = useState('');
|
||||
|
||||
const {apiRequest} = useRequest();
|
||||
|
||||
useEffect(() => {
|
||||
setRequestDates(getReports(moment()))
|
||||
});
|
||||
|
||||
useEffect(async () => {
|
||||
if (!requestDates) {
|
||||
return
|
||||
}
|
||||
apiRequest(`/reports/reports-by-date?${requestDates}&user_id=${localStorage.getItem('id')}`)
|
||||
.then((reports) => {
|
||||
let spendTime = 0;
|
||||
reports.map((report) => {
|
||||
if (report.spendTime) {
|
||||
spendTime += Number(report.spendTime)
|
||||
}
|
||||
});
|
||||
setTotalHours(spendTime);
|
||||
setReports(reports)
|
||||
})
|
||||
}, [requestDates]);
|
||||
|
||||
useEffect(() => {
|
||||
setMonth(currentMonth)
|
||||
}, [month]);
|
||||
|
||||
return (
|
||||
<div className='profile__calendar'>
|
||||
<ProfileHeader/>
|
||||
<div className='container'>
|
||||
<h2 className='summary__title'>Ваши отчеты</h2>
|
||||
<div className='summary__info'>
|
||||
<div className='summary__person'>
|
||||
<img src={profileInfo.photo} className='summary__avatar' alt='avatar'/>
|
||||
<p className='summary__name'>{profileInfo.fio} {profileInfo.specification}</p>
|
||||
</div>
|
||||
<Link to='/report'>
|
||||
<button className="calendar__btn" onClick={() => {
|
||||
dispatch(setReportDate(''))
|
||||
}}>Заполнить отчет за день</button>
|
||||
</Link>
|
||||
</div>
|
||||
<div className='row'>
|
||||
<div className='col-12 col-xl-12'>
|
||||
<ProfileCalendarComponent reportsDates={reports} />
|
||||
<p className='calendar__hours'>
|
||||
{month} : <span> {totalHours} часов </span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
};
|
119
src/components/ProfileCalendar/ProfileCalendarComponent.js
Normal file
@ -0,0 +1,119 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
// import ellipse from '../../images/ellipse.png'
|
||||
import rectangle from '../../images/rectangle__calendar.png'
|
||||
import calendarIcon from '../../images/calendar_icon.png'
|
||||
import moment from 'moment'
|
||||
import 'moment/locale/ru'
|
||||
import { calendarHelper, currentMonthAndDay} from '../Calendar/calendarHelper'
|
||||
import { setReportDate } from '../../redux/reportSlice';
|
||||
import {useDispatch} from "react-redux";
|
||||
import {Link} from "react-router-dom";
|
||||
|
||||
import './../Calendar/calendarComponent.scss'
|
||||
|
||||
export const ProfileCalendarComponent = ({reportsDates}) => {
|
||||
const dispatch = useDispatch();
|
||||
const [value, setValue] = useState(moment())
|
||||
const [calendar, setCalendar] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
setCalendar(calendarHelper(value))
|
||||
}, [value])
|
||||
|
||||
// function beforeToday(day) {
|
||||
// return day.isBefore(new Date(), 'day')
|
||||
// }
|
||||
|
||||
function isToday(day) {
|
||||
return day.isSame(new Date(), 'day')
|
||||
}
|
||||
|
||||
function correctDay(day) {
|
||||
if (day < 10) {
|
||||
return `0${day}`
|
||||
} return day
|
||||
}
|
||||
|
||||
function dayStyles(day) {
|
||||
if (value < day) return `block`
|
||||
for (const date of reportsDates) {
|
||||
if (`${new Date(day).getFullYear()}-${correctDay(new Date(day).getMonth() + 1)}-${correctDay(new Date(day).getDate())}` === date.date) {
|
||||
return `before`
|
||||
}
|
||||
}
|
||||
if (day.day() === 6 || day.day() === 0) return `selected`
|
||||
if (isToday(day)) return `today`
|
||||
return 'pass'
|
||||
}
|
||||
|
||||
function correctRoute(day) {
|
||||
for (const date of reportsDates) {
|
||||
if (`${new Date(day).getFullYear()}-${correctDay(new Date(day).getMonth() + 1)}-${correctDay(new Date(day).getDate())}` === date.date) {
|
||||
return `../../view/report`
|
||||
}
|
||||
}
|
||||
return '../../report'
|
||||
}
|
||||
|
||||
// function prevMonth() {
|
||||
// return value.clone().subtract(1, 'month')
|
||||
// }
|
||||
//
|
||||
// function nextMonth() {
|
||||
// return value.clone().add(1, 'month');
|
||||
// }
|
||||
|
||||
return (
|
||||
<div className='calendar-component'>
|
||||
<div className='calendar-component__header'>
|
||||
<h3>Мои отчеты</h3>
|
||||
{/*<div className='calendar-component__header-box'>*/}
|
||||
{/* <img src={ellipse} alt='' />*/}
|
||||
{/* <span onClick={() => setValue(prevMonth())}>{prevMonth().format('MMMM')}</span>*/}
|
||||
{/*</div>*/}
|
||||
{/*<div className='calendar-component__header-box'>*/}
|
||||
{/* <img src={ellipse} alt='' />*/}
|
||||
{/* <span onClick={() => setValue(nextMonth())}>{nextMonth().format('MMMM')}</span>*/}
|
||||
{/*</div>*/}
|
||||
</div>
|
||||
|
||||
<div className='calendar-component__rectangle'>
|
||||
<img src={rectangle} alt='' />
|
||||
</div>
|
||||
|
||||
<div className='calendar-component__body'>
|
||||
<div>
|
||||
<p>Пн</p>
|
||||
<p>Вт</p>
|
||||
<p>Ср</p>
|
||||
<p>Чт</p>
|
||||
<p>Пт</p>
|
||||
<p>Сб</p>
|
||||
<p>Вс</p>
|
||||
</div>
|
||||
|
||||
<div className='calendar-component__form'>
|
||||
{calendar.map((week) =>
|
||||
week.map((day) => (
|
||||
<button
|
||||
onClick={() => {
|
||||
dispatch(setReportDate(day))
|
||||
}}
|
||||
key={day}
|
||||
className={dayStyles(day)}
|
||||
name={day.format('dddd')}
|
||||
id='btn'
|
||||
>
|
||||
<Link to={correctRoute(day)}>
|
||||
<img className={'calendar__icon'} src={calendarIcon} alt='' />
|
||||
{currentMonthAndDay(day)}
|
||||
</Link>
|
||||
</button>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
43
src/components/ProfileCalendar/profileCalendar.scss
Normal file
@ -0,0 +1,43 @@
|
||||
.profile__calendar {
|
||||
background: #F1F1F1;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
font-family: "LabGrotesque", sans-serif;
|
||||
|
||||
.container {
|
||||
max-width: 1160px;
|
||||
margin-top: 23px;
|
||||
|
||||
@media (max-width: 570px) {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.summary__info {
|
||||
padding-right: 25px;
|
||||
}
|
||||
.profile__calendar {
|
||||
margin-top: 20px;
|
||||
}
|
||||
&__back {
|
||||
text-decoration: none !important;
|
||||
color: black !important;
|
||||
font-size: 14px;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
column-gap: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__profile {
|
||||
margin-top: 42px;
|
||||
}
|
||||
|
||||
&__btn {
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
}
|
||||
}
|
71
src/components/ProfileHeader/ProfileHeader.js
Normal file
@ -0,0 +1,71 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {useNavigate, NavLink} from "react-router-dom";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
|
||||
import {Loader} from '../Loader/Loader'
|
||||
|
||||
import {auth, getProfileInfo, setProfileInfo} from "../../redux/outstaffingSlice";
|
||||
import {getRole} from "../../redux/roleSlice";
|
||||
|
||||
|
||||
|
||||
import './profileHeader.scss'
|
||||
import {useRequest} from "../../hooks/useRequest";
|
||||
|
||||
|
||||
export const ProfileHeader = () => {
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {apiRequest} = useRequest();
|
||||
|
||||
const userRole = useSelector(getRole);
|
||||
const profileInfo = useSelector(getProfileInfo);
|
||||
|
||||
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
apiRequest(`/profile/${localStorage.getItem('cardId')}`)
|
||||
.then((profileInfo) =>
|
||||
dispatch(setProfileInfo(profileInfo))
|
||||
);
|
||||
|
||||
}, [dispatch]);
|
||||
|
||||
const handler = () => {
|
||||
setIsLoggingOut(true);
|
||||
localStorage.clear();
|
||||
dispatch(auth(false));
|
||||
setIsLoggingOut(false);
|
||||
navigate(userRole === 'ROLE_DEV' ? '/authdev' : '/auth')
|
||||
};
|
||||
|
||||
return (
|
||||
<header className='profileHeader'>
|
||||
<div className='profileHeader__head'>
|
||||
<div className='profileHeader__container'>
|
||||
<h2 className='profileHeader__title'>itguild.<span>для разработчиков</span></h2>
|
||||
<button onClick={handler} className='profileHeader__logout'>
|
||||
{isLoggingOut ? <Loader/> : 'Выйти'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='profileHeader__info'>
|
||||
<div className='profileHeader__container'>
|
||||
<nav className='profileHeader__nav'>
|
||||
<NavLink end to={'/profile/summary'}>Резюме</NavLink>
|
||||
<NavLink end to={'/profile'}>Отчетность</NavLink>
|
||||
<NavLink end to={'/'}>Трекер</NavLink>
|
||||
<NavLink end to={'/'}>Выплаты</NavLink>
|
||||
<NavLink end to={'/'}>Настройки</NavLink>
|
||||
</nav>
|
||||
|
||||
<div className='profileHeader__personalInfo'>
|
||||
<h3 className='profileHeader__personalInfoName'>{profileInfo.fio}</h3>
|
||||
<img src={profileInfo.photo} className='profileHeader__personalInfoAvatar' alt='avatar'/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
};
|
101
src/components/ProfileHeader/profileHeader.scss
Normal file
@ -0,0 +1,101 @@
|
||||
.profileHeader {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: 'LabGrotesque', sans-serif;
|
||||
|
||||
&__head {
|
||||
background: #E1FCCF;
|
||||
}
|
||||
|
||||
&__container {
|
||||
max-width: 1160px;
|
||||
padding: 0 10px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-weight: 700;
|
||||
font-size: 20px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 0;
|
||||
|
||||
span {
|
||||
color: #52B709;
|
||||
}
|
||||
}
|
||||
|
||||
&__logout {
|
||||
background: none;
|
||||
border: none;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
&__info {
|
||||
background: #FFFFFF;
|
||||
}
|
||||
|
||||
&__nav {
|
||||
display: flex;
|
||||
column-gap: 30px;
|
||||
|
||||
.active {
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none !important;
|
||||
cursor: pointer;
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
line-height: 32px;
|
||||
color: #807777 !important;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
color: #261a1a !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
column-gap: 15px;
|
||||
|
||||
a {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__personalInfo {
|
||||
display: flex;
|
||||
column-gap: 20px;
|
||||
align-items: center;
|
||||
|
||||
&Name {
|
||||
margin-bottom: 0;
|
||||
font-size: 12px;
|
||||
line-height: 32px;
|
||||
max-width: 150px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@media (max-width: 600px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&Avatar {
|
||||
width: 37px;
|
||||
height: 37px;
|
||||
border-radius: 100px;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,16 +8,16 @@ export const ProtectedRoute = ({ component: Component, ...rest }) => {
|
||||
const existingToken = localStorage.getItem('auth_token')
|
||||
const expiresAt = localStorage.getItem('access_token_expired_at')
|
||||
|
||||
const isTokenAlive = (existingToken && expiresAt && new Date(expiresAt).getTime() > (new Date()).getTime());
|
||||
const isTokenAlive = !isAuth && (existingToken && expiresAt && new Date(expiresAt).getTime() > (new Date()).getTime());
|
||||
|
||||
return (
|
||||
<Route
|
||||
{...rest}
|
||||
render={props =>
|
||||
( isAuth || isTokenAlive) ? (
|
||||
// ( isAuth || isTokenAlive) ? (
|
||||
<Component {...props} />
|
||||
) : <Redirect to='/auth' />
|
||||
// ) : <Redirect to='/auth' />
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
@ -1,58 +1,100 @@
|
||||
import React, { useState } from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import { fetchPost } from '../../server/server'
|
||||
import { useHistory, useParams, Redirect } from 'react-router-dom'
|
||||
import { Loader } from '../Loader/Loader'
|
||||
import { auth } from '../../redux/outstaffingSlice'
|
||||
import { getRole } from '../../redux/roleSlice'
|
||||
import React, {useState} from 'react'
|
||||
import {useSelector} from 'react-redux'
|
||||
import {Link} from 'react-router-dom'
|
||||
|
||||
import {Loader} from '../Loader/Loader'
|
||||
import {currentMonthAndDay} from '../Calendar/calendarHelper'
|
||||
import {Footer} from "../Footer/Footer";
|
||||
import {ProfileHeader} from "../ProfileHeader/ProfileHeader";
|
||||
|
||||
import {useRequest} from "../../hooks/useRequest";
|
||||
|
||||
import {getReportDate} from '../../redux/reportSlice'
|
||||
|
||||
import calendarIcon from '../../images/calendar_icon.png'
|
||||
import ellipse from '../../images/ellipse.png'
|
||||
import remove from '../../images/remove.png'
|
||||
import addIcon from '../../images/addIcon.png'
|
||||
import { currentMonthAndDayReportPage } from '../Calendar/calendarHelper'
|
||||
import arrow from "../../images/right-arrow.png";
|
||||
|
||||
import './reportForm.scss'
|
||||
|
||||
const getCreatedDate = () => {
|
||||
const date = new Date();
|
||||
const dd = String(date.getDate()).padStart(2, '0')
|
||||
const mm = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const yyyy = date.getFullYear()
|
||||
|
||||
const getCreatedDate = (day) => {
|
||||
if (day) {
|
||||
return `${new Date(day).getFullYear()}-${new Date(day).getMonth() + 1}-${new Date(day).getDate()}`
|
||||
} else {
|
||||
const date = new Date();
|
||||
const dd = String(date.getDate()).padStart(2, '0');
|
||||
const mm = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const yyyy = date.getFullYear();
|
||||
return `${yyyy}-${mm}-${dd}`
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const ReportForm = () => {
|
||||
const dispatch = useDispatch()
|
||||
const history = useHistory()
|
||||
const role = useSelector(getRole)
|
||||
const reportDate = useSelector(getReportDate);
|
||||
|
||||
const [isFetching, setIsFetching] = useState(false)
|
||||
const [reportSuccess, setReportSuccess] = useState(false)
|
||||
const {apiRequest} = useRequest();
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
const [reportSuccess, setReportSuccess] = useState(false);
|
||||
|
||||
const [inputs, setInputs] = useState([ { task: '', hours_spent: '', minutes_spent: 0 } ]);
|
||||
const [inputs, setInputs] = useState([{task: '', hours_spent: '', minutes_spent: 0}]);
|
||||
const [troublesInputValue, setTroublesInputValue] = useState('');
|
||||
const [scheduledInputValue, setScheduledInputValue] = useState('');
|
||||
|
||||
const addInput = () => {
|
||||
setInputs((prev) => [...prev, { task: '', hours_spent: '', minutes_spent: 0 }])
|
||||
}
|
||||
setInputs((prev) => [...prev, {task: '', hours_spent: '', minutes_spent: 0}])
|
||||
};
|
||||
|
||||
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) => {
|
||||
if (indexRemove !== 0) {
|
||||
setInputs((prev) => prev.filter((el, index) => index !== indexRemove))
|
||||
}
|
||||
};
|
||||
|
||||
const handler = () => {
|
||||
apiRequest('/reports/create', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
tasks: inputs,
|
||||
difficulties: troublesInputValue,
|
||||
tomorrow: scheduledInputValue,
|
||||
created_at: getCreatedDate(reportDate),
|
||||
status: 1,
|
||||
},
|
||||
}).then((res) => {
|
||||
if (res.status === 200) {
|
||||
setReportSuccess(true);
|
||||
setTimeout(() => setReportSuccess(false), 2000)
|
||||
}
|
||||
setInputs(() => []);
|
||||
setTroublesInputValue('');
|
||||
setScheduledInputValue('');
|
||||
setIsFetching(false);
|
||||
setInputs(() => [{task: '', hours_spent: '', minutes_spent: 0}]);
|
||||
})
|
||||
};
|
||||
|
||||
return (
|
||||
<section className='report-form'>
|
||||
<div className='row'>
|
||||
<div className='col-xl-12'>
|
||||
<ProfileHeader/>
|
||||
<div className='container'>
|
||||
<h2 className='summary__title'>Ваши отчеты - <span>добавить отчет</span></h2>
|
||||
<div>
|
||||
<div className='report__head'>
|
||||
<Link className='calendar__back' to={`/profile/calendar`}>
|
||||
<img src={arrow} alt=''/><p>Вернуться</p>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='report-form__content'>
|
||||
<div className='report-form__block'>
|
||||
<div className='report-form__block-title'>
|
||||
<h2>Добавить отчет</h2>
|
||||
<h2>Добавление отчета за день</h2>
|
||||
<h3>Дата заполнения отчета:</h3>
|
||||
</div>
|
||||
<div className='report-form__block-img'>
|
||||
@ -61,15 +103,14 @@ const ReportForm = () => {
|
||||
src={calendarIcon}
|
||||
alt=''
|
||||
/>
|
||||
{currentMonthAndDayReportPage()}
|
||||
{/*{currentMonthAndDayReportPage()}*/}
|
||||
{reportDate ? currentMonthAndDay(reportDate) : getCreatedDate()}
|
||||
</div>
|
||||
<div className='report-form__task-list'>
|
||||
<img src={ellipse} alt='' />
|
||||
<img src={ellipse} alt=''/>
|
||||
<span>Какие задачи были выполнены?</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='row'>
|
||||
<div className='col-8'>
|
||||
@ -84,37 +125,38 @@ const ReportForm = () => {
|
||||
return (
|
||||
<form id={'input'} key={`input__${index}`} className='report-form__task-form'>
|
||||
<div className='report-form__task-number'>
|
||||
{index+1}.
|
||||
{index + 1}.
|
||||
</div>
|
||||
<div className='report-form__task-input report-form__task-input--description'>
|
||||
<input name='text' type='text' onChange={ e => setInputs(inputs.map( (input, inputIndex) => {
|
||||
<input 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 name='number' type='number' min='1' onChange={ e => setInputs(inputs.map( (input, inputIndex) => {
|
||||
<input 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>
|
||||
<div className='report-form__task-remove'>
|
||||
<img onClick={() => deleteInput(index)} src={remove} alt='' />
|
||||
<img onClick={() => deleteInput(index)} src={remove} alt=''/>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
})}
|
||||
|
||||
<div className='report-form__form-add'>
|
||||
<img onClick={addInput} src={addIcon} alt='' />
|
||||
<img onClick={addInput} src={addIcon} alt=''/>
|
||||
<span>Добавить еще </span>
|
||||
</div>
|
||||
</div>
|
||||
@ -125,49 +167,23 @@ const ReportForm = () => {
|
||||
<div className='col-12'>
|
||||
<div className='report-form__input-box'>
|
||||
<div className='report-form__troubles'>
|
||||
<img src={ellipse} alt='' />
|
||||
<img src={ellipse} alt=''/>
|
||||
<span>Какие сложности возникли?</span>
|
||||
</div>
|
||||
<input type='text' value={troublesInputValue} onChange={e => setTroublesInputValue(e.target.value)} />
|
||||
<input type='text' value={troublesInputValue} onChange={e => setTroublesInputValue(e.target.value)}/>
|
||||
<div className='report-form__scheduled'>
|
||||
<img src={ellipse} alt='' />
|
||||
<img src={ellipse} alt=''/>
|
||||
<span>Что планируется сделать завтра?</span>
|
||||
</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 className='row'>
|
||||
<div className='col-12'>
|
||||
<div className='report-form__footer'>
|
||||
<button className='report-form__footer-btn' onClick={() => {
|
||||
fetchPost({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/reports/create`,
|
||||
history,
|
||||
role,
|
||||
body: {
|
||||
tasks: inputs,
|
||||
difficulties: troublesInputValue,
|
||||
tomorrow: scheduledInputValue,
|
||||
created_at: getCreatedDate(),
|
||||
status: 1,
|
||||
},
|
||||
logout: () => dispatch(auth(false))
|
||||
}).then((res) =>
|
||||
res.json().then((resJSON) => {
|
||||
if(res.status === 200) {
|
||||
setReportSuccess(true)
|
||||
setTimeout(()=> setReportSuccess(false),2000)
|
||||
}
|
||||
setInputs( () => [] )
|
||||
setTroublesInputValue('');
|
||||
setScheduledInputValue('');
|
||||
setIsFetching(false)
|
||||
setInputs(() => [ { task: '', hours_spent: '', minutes_spent: 0 } ]);
|
||||
})
|
||||
)
|
||||
}}>
|
||||
{isFetching ? <Loader /> : 'Отправить'}
|
||||
<button className='report-form__footer-btn' onClick={() => handler()}>
|
||||
{isFetching ? <Loader/> : 'Отправить'}
|
||||
</button>
|
||||
<p className='report-form__footer-text'>
|
||||
Всего за день : <span>{totalHours} часов</span>
|
||||
@ -178,8 +194,11 @@ const ReportForm = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Footer/>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default ReportForm
|
||||
|
@ -1,9 +1,51 @@
|
||||
.report-form {
|
||||
background: #F1F1F1;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
font-family: "LabGrotesque", sans-serif;
|
||||
|
||||
.container {
|
||||
max-width: 1160px;
|
||||
margin-top: 23px;
|
||||
|
||||
@media (max-width: 570px) {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
background: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
margin: 25px 0 80px;
|
||||
padding: 50px 40px;
|
||||
}
|
||||
|
||||
.report__head {
|
||||
margin-top: 20px;
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
grid-column-gap: 30px;
|
||||
column-gap: 30px;
|
||||
margin-top: 20px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
font-weight: 500;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__block-title {
|
||||
margin-top: 76px;
|
||||
|
||||
h2 {
|
||||
color: #282828;
|
||||
color: #52B709;
|
||||
font-family: 'GT Eesti Pro Display';
|
||||
font-size: 3.3em;
|
||||
font-weight: 700;
|
||||
|
@ -1,49 +1,46 @@
|
||||
import React, { useState } from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import React, {useState} from 'react'
|
||||
import {useSelector, useDispatch} from 'react-redux'
|
||||
import Select from 'react-select'
|
||||
import { Loader } from '../Loader/Loader'
|
||||
import style from './TagSelect.module.css'
|
||||
import {Loader} from '../Loader/Loader'
|
||||
import {useRequest} from "../../hooks/useRequest";
|
||||
import {
|
||||
selectedItems,
|
||||
selectItems,
|
||||
selectTags,
|
||||
filteredCandidates,
|
||||
setPositionId,
|
||||
auth
|
||||
setPositionId
|
||||
} from '../../redux/outstaffingSlice'
|
||||
import { fetchGet } from '../../server/server'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { getRole } from '../../redux/roleSlice'
|
||||
|
||||
import style from './TagSelect.module.css'
|
||||
|
||||
|
||||
const TagSelect = () => {
|
||||
const history = useHistory
|
||||
const role = useSelector(getRole)
|
||||
const [searchLoading, setSearchLoading] = useState(false)
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const itemsArr = useSelector(selectItems)
|
||||
const [searchLoading, setSearchLoading] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const tagsArr = useSelector(selectTags)
|
||||
const {apiRequest} = useRequest();
|
||||
|
||||
const handleSubmit = ({ dispatch, setSearchLoading }) => {
|
||||
setSearchLoading(true)
|
||||
const itemsArr = useSelector(selectItems);
|
||||
const tagsArr = useSelector(selectTags);
|
||||
|
||||
dispatch(setPositionId(null))
|
||||
const filterItemsId = itemsArr.map((item) => item.id).join()
|
||||
const handleSubmit = ({dispatch, setSearchLoading}) => {
|
||||
setSearchLoading(true);
|
||||
|
||||
fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/profile?skills=`,
|
||||
params: filterItemsId,
|
||||
history,
|
||||
role,
|
||||
logout: () => dispatch(auth(false))
|
||||
dispatch(setPositionId(null));
|
||||
const filterItemsId = itemsArr.map((item) => item.id).join();
|
||||
const params = filterItemsId ? {skill: filterItemsId} : '';
|
||||
|
||||
|
||||
apiRequest('/profile', {
|
||||
params: {...params, limit: 1000},
|
||||
}).then((el) => {
|
||||
dispatch(filteredCandidates(el))
|
||||
dispatch(filteredCandidates(el));
|
||||
setSearchLoading(false)
|
||||
})
|
||||
|
||||
// dispatch(selectedItems([]));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -56,7 +53,7 @@ const TagSelect = () => {
|
||||
<div className={style.search__box}>
|
||||
<Select
|
||||
value={itemsArr}
|
||||
onChange={(value) => dispatch(selectedItems(value))}
|
||||
onChange={(value) => {console.log(value) ;return dispatch(selectedItems(value))}}
|
||||
isMulti
|
||||
name='tags'
|
||||
className={style.select}
|
||||
@ -73,11 +70,11 @@ const TagSelect = () => {
|
||||
}
|
||||
/>
|
||||
<button
|
||||
onClick={() => handleSubmit({ dispatch, setSearchLoading })}
|
||||
onClick={() => handleSubmit({dispatch, setSearchLoading})}
|
||||
type='submit'
|
||||
className={style.search__submit}
|
||||
>
|
||||
{searchLoading ? <Loader width={30} height={30} /> : 'Поиск'}
|
||||
{searchLoading ? <Loader width={30} height={30}/> : 'Поиск'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -85,6 +82,6 @@ const TagSelect = () => {
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default TagSelect
|
||||
|
@ -1,56 +1,52 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ContentTitle } from "../ContentTitle/ContentTitle"
|
||||
import { ContentButton } from "../ContentButton/ContentButton"
|
||||
import { BookkeepingFormField } from "../BookkeepingFormField/BookkeepingFormField"
|
||||
import { BookkepingSelect } from '../BookkepingSelect/BookkepingSelect';
|
||||
import { BookkepingInput } from '../BookkepingInput/BookkepingInput';
|
||||
import { fetchGet } from '../../../../server/server'
|
||||
import { Link } from "react-router-dom"
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Link} from "react-router-dom"
|
||||
import {ContentTitle} from "../ContentTitle/ContentTitle"
|
||||
import {ContentButton} from "../ContentButton/ContentButton"
|
||||
import {BookkeepingFormField} from "../BookkeepingFormField/BookkeepingFormField"
|
||||
import {BookkepingSelect} from '../BookkepingSelect/BookkepingSelect';
|
||||
import {BookkepingInput} from '../BookkepingInput/BookkepingInput';
|
||||
|
||||
import {useRequest} from "../../../../hooks/useRequest";
|
||||
|
||||
import "./actContent.css"
|
||||
|
||||
export const ActContent = ()=> {
|
||||
export const ActContent = () => {
|
||||
|
||||
const [templates, setTemplates] = useState([])
|
||||
const [selectedTemplate, setSelectedTemplate] = useState()
|
||||
const [templatedFields, setTemplatedFields] = useState([])
|
||||
const [templates, setTemplates] = useState([]);
|
||||
const [selectedTemplate, setSelectedTemplate] = useState();
|
||||
const [templatedFields, setTemplatedFields] = useState([]);
|
||||
|
||||
const {apiRequest} = useRequest();
|
||||
|
||||
useEffect(() => {
|
||||
fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/template/get-template-list`,
|
||||
}).then((res) => {
|
||||
setTemplates(res)
|
||||
})
|
||||
}, [])
|
||||
apiRequest('/template/get-template-list')
|
||||
.then(res => setTemplates(res))
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedTemplate === undefined) {
|
||||
return
|
||||
}
|
||||
fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/template/get-template-fields?template_id=${selectedTemplate}`,
|
||||
})
|
||||
.then((res) => {
|
||||
setTemplatedFields(res[0].templateDocumentFields)
|
||||
})
|
||||
}, [selectedTemplate])
|
||||
apiRequest(`/template/get-template-fields?template_id=${selectedTemplate}`)
|
||||
.then(res => setTemplatedFields(res[0].templateDocumentFields))
|
||||
}, [selectedTemplate]);
|
||||
|
||||
|
||||
|
||||
|
||||
return(
|
||||
return (
|
||||
<div>
|
||||
<div className="content__info">
|
||||
<ContentTitle title="Создание акта" description="# Описание" />
|
||||
<ContentTitle title="Создание акта" description="# Описание"/>
|
||||
<div className="content__info-main">
|
||||
<form className='contract'>
|
||||
<div className="contract__create">
|
||||
<div className="contract__title">Создание акта №</div>
|
||||
<input type="text" className="contract__number" placeholder="#" />
|
||||
<input type="text" className="contract__number" placeholder="#"/>
|
||||
<span>от</span>
|
||||
<input type="date" className="contract__date" />
|
||||
<input type="date" className="contract__date"/>
|
||||
</div>
|
||||
|
||||
<BookkeepingFormField title="Шаблон документа"
|
||||
<BookkeepingFormField
|
||||
title="Шаблон документа"
|
||||
Component={BookkepingSelect}
|
||||
innerComponentProps={{
|
||||
onSelect: setSelectedTemplate,
|
||||
@ -60,12 +56,14 @@ export const ActContent = ()=> {
|
||||
}}
|
||||
action={{
|
||||
text: "Добавить свой шаблон",
|
||||
method: () => {}
|
||||
method: () => {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
{templatedFields.map((field, index ) =>
|
||||
<BookkeepingFormField title={field.field.title} key={index}
|
||||
{templatedFields.map((field, index) =>
|
||||
<BookkeepingFormField
|
||||
title={field.field.title} key={index}
|
||||
Component={BookkepingInput}
|
||||
innerComponentProps={{
|
||||
placeholder: "Введите данные",
|
||||
@ -74,9 +72,9 @@ export const ActContent = ()=> {
|
||||
)}
|
||||
|
||||
|
||||
|
||||
<div className="content__btn-list">
|
||||
<ContentButton styles={{ width: "290px",
|
||||
<ContentButton styles={{
|
||||
width: "290px",
|
||||
height: "75px",
|
||||
boxShadow: "6px 5px 20px rgba(182, 75, 62, 0.21)",
|
||||
borderRadius: "38px",
|
||||
@ -86,7 +84,8 @@ export const ActContent = ()=> {
|
||||
}}>Сохранить</ContentButton>
|
||||
<Link to="/documents" className="link-act-button">
|
||||
<div className='act-Button'>
|
||||
<ContentButton styles={{color: "#282828",
|
||||
<ContentButton styles={{
|
||||
color: "#282828",
|
||||
marginLeft: "40px",
|
||||
background: "none",
|
||||
border: "none"
|
||||
@ -99,4 +98,4 @@ export const ActContent = ()=> {
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
};
|
@ -1,53 +1,50 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { ContentTitle } from "../ContentTitle/ContentTitle"
|
||||
import { ContentButton } from "../ContentButton/ContentButton"
|
||||
import { BookkeepingFormField } from "../BookkeepingFormField/BookkeepingFormField"
|
||||
import { BookkepingSelect } from '../BookkepingSelect/BookkepingSelect';
|
||||
import { BookkepingInput } from '../BookkepingInput/BookkepingInput';
|
||||
import { fetchGet } from '../../../../server/server'
|
||||
import { Link } from "react-router-dom"
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {ContentTitle} from "../ContentTitle/ContentTitle"
|
||||
import {ContentButton} from "../ContentButton/ContentButton"
|
||||
import {BookkeepingFormField} from "../BookkeepingFormField/BookkeepingFormField"
|
||||
import {BookkepingSelect} from '../BookkepingSelect/BookkepingSelect';
|
||||
import {BookkepingInput} from '../BookkepingInput/BookkepingInput';
|
||||
import {Link} from "react-router-dom"
|
||||
import {useRequest} from "../../../../hooks/useRequest";
|
||||
|
||||
export const ContractContent = () => {
|
||||
|
||||
const [templates, setTemplates] = useState([])
|
||||
const [selectedTemplate, setSelectedTemplate] = useState()
|
||||
const [templatedFields, setTemplatedFields] = useState([])
|
||||
const [templates, setTemplates] = useState([]);
|
||||
const [selectedTemplate, setSelectedTemplate] = useState();
|
||||
const [templatedFields, setTemplatedFields] = useState([]);
|
||||
|
||||
const {apiRequest} = useRequest();
|
||||
|
||||
useEffect(() => {
|
||||
fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/document/get-document-list`,
|
||||
}).then((res) => {
|
||||
setTemplates(res)
|
||||
})
|
||||
}, [])
|
||||
apiRequest(`/document/get-document-list`)
|
||||
.then(res => setTemplates(res))
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedTemplate === undefined) {
|
||||
return
|
||||
}
|
||||
fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/document/get-document?document_id=${selectedTemplate}`,
|
||||
})
|
||||
.then((res) => {
|
||||
setTemplatedFields(res[0].templateDocumentFields)
|
||||
})
|
||||
}, [selectedTemplate])
|
||||
apiRequest(`/document/get-document?document_id=${selectedTemplate}`)
|
||||
.then(res => setTemplatedFields(res[0].templateDocumentFields)
|
||||
)
|
||||
}, [selectedTemplate]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="content__info">
|
||||
<ContentTitle title="Создание договора" description="# Описание" />
|
||||
<ContentTitle title="Создание договора" description="# Описание"/>
|
||||
<div className="content__info-main">
|
||||
|
||||
|
||||
<form className='contract'>
|
||||
<div className="contract__create">
|
||||
<div className="contract__title">Создание договора №</div>
|
||||
<input type="text" className="contract__number" placeholder="#" />
|
||||
<input type="text" className="contract__number" placeholder="#"/>
|
||||
<span>от</span>
|
||||
<input type="date" className="contract__date" />
|
||||
<input type="date" className="contract__date"/>
|
||||
</div>
|
||||
<BookkeepingFormField title="Шаблон документа"
|
||||
<BookkeepingFormField
|
||||
title="Шаблон документа"
|
||||
Component={BookkepingSelect}
|
||||
innerComponentProps={{
|
||||
onSelect: setSelectedTemplate,
|
||||
@ -57,12 +54,14 @@ export const ContractContent = () => {
|
||||
}}
|
||||
action={{
|
||||
text: "Добавить свой шаблон",
|
||||
method: () => {}
|
||||
method: () => {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
{templatedFields.map((field, index ) =>
|
||||
<BookkeepingFormField title={field.field.title} key={index}
|
||||
{templatedFields.map((field, index) =>
|
||||
<BookkeepingFormField
|
||||
title={field.field.title} key={index}
|
||||
Component={BookkepingInput}
|
||||
innerComponentProps={{
|
||||
placeholder: "Введите данные",
|
||||
@ -71,7 +70,8 @@ export const ContractContent = () => {
|
||||
)}
|
||||
|
||||
<div className="content__btn-list">
|
||||
<ContentButton styles={{ width: "290px",
|
||||
<ContentButton styles={{
|
||||
width: "290px",
|
||||
height: "75px",
|
||||
boxShadow: "6px 5px 20px rgba(182, 75, 62, 0.21)",
|
||||
borderRadius: "38px",
|
||||
@ -81,7 +81,8 @@ export const ContractContent = () => {
|
||||
}}>Сохранить</ContentButton>
|
||||
<Link to="/documents" className="link-act-button">
|
||||
<div className='act-Button'>
|
||||
<ContentButton styles={{color: "#282828",
|
||||
<ContentButton styles={{
|
||||
color: "#282828",
|
||||
marginLeft: "40px",
|
||||
background: "none",
|
||||
border: "none"
|
||||
@ -94,4 +95,4 @@ export const ContractContent = () => {
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
};
|
@ -6,7 +6,7 @@ import {selectedTest, selectUserInfo} from "../../../redux/quizSlice";
|
||||
|
||||
export const HeaderPageTestsQuiz = ({isVisibilityButton}) => {
|
||||
|
||||
const test = useSelector(selectedTest)
|
||||
const test = useSelector(selectedTest);
|
||||
const userInfo = useSelector(selectUserInfo);
|
||||
|
||||
return (
|
||||
@ -23,8 +23,8 @@ export const HeaderPageTestsQuiz = ({isVisibilityButton}) => {
|
||||
</div>
|
||||
</div>
|
||||
{isVisibilityButton &&
|
||||
<Link to={'/quiz-instruction'} className='quiz-btn quiz-btn_restriction'>Пройти</Link>}
|
||||
<Link to={'/quiz/instruction'} className='quiz-btn quiz-btn_restriction'>Пройти</Link>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
};
|
@ -1,33 +1,29 @@
|
||||
import {useEffect} from 'react'
|
||||
import {useDispatch} from 'react-redux'
|
||||
import {useSelector} from 'react-redux'
|
||||
import {fetchGet} from '../../../server/server'
|
||||
import React, {useEffect} from 'react'
|
||||
import {useDispatch, useSelector} from 'react-redux'
|
||||
import {selectUserInfo, setQuestionnairesList, setUserInfo} from "../../../redux/quizSlice";
|
||||
import {useRequest} from "../../../hooks/useRequest";
|
||||
import './quiz.scss'
|
||||
import {selectUserInfo, setQuestionnairesList, setUserInfo,} from "../../../redux/quizSlice";
|
||||
|
||||
export const HeaderQuiz = ({header}) => {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const dispatch = useDispatch();
|
||||
const userId = localStorage.getItem('id');
|
||||
const userInfo = useSelector(selectUserInfo);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setUserInfo(userId))
|
||||
}, [dispatch])
|
||||
const {apiRequest} = useRequest();
|
||||
|
||||
useEffect(() => {
|
||||
fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/user-questionnaire/questionnaires-list?user_id=${userId}`,
|
||||
Origin: `${process.env.REACT_APP_BASE_URL}`,
|
||||
}
|
||||
).then(response => {
|
||||
dispatch(setQuestionnairesList(response))
|
||||
})
|
||||
}, [dispatch])
|
||||
dispatch(setUserInfo(userId))
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
apiRequest(`/user-questionnaire/questionnaires-list?user_id=${userId}`)
|
||||
.then(res => dispatch(setQuestionnairesList(res)))
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ userInfo?.status === 500 ? <div className="error-msg">{userInfo.message}</div> :
|
||||
{userInfo?.status === 500 ? <div className="error-msg">{userInfo.message}</div> :
|
||||
<div className="header-quiz">
|
||||
<div className="header-quiz__container">
|
||||
{!userInfo ? <h2>Loading...</h2> :
|
||||
@ -47,4 +43,4 @@ export const HeaderQuiz = ({header}) => {
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
@ -1,27 +1,28 @@
|
||||
import {Link} from 'react-router-dom'
|
||||
import {CodeSnippetlighter} from '../../../pages/CodeSnippetPage'
|
||||
import comment from './../../../images/comment.jpg'
|
||||
import './quiz.scss'
|
||||
import {useEffect, useState} from "react";
|
||||
import {useSelector} from "react-redux";
|
||||
import {selectedTest} from "../../../redux/quizSlice";
|
||||
import {fetchGet} from "../../../server/server";
|
||||
|
||||
import {useRequest} from "../../../hooks/useRequest";
|
||||
|
||||
export const Instruction = () => {
|
||||
|
||||
const [countQuestions, setCountQuestions] = useState(null)
|
||||
const test = useSelector(selectedTest)
|
||||
const [countQuestions, setCountQuestions] = useState(null);
|
||||
const test = useSelector(selectedTest);
|
||||
|
||||
useEffect(async () => {
|
||||
const {apiRequest} = useRequest();
|
||||
|
||||
const response = await fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/user-questionnaire/get-question-number?user_questionnaire_uuid=${test.uuid}`,
|
||||
Origin: `${process.env.REACT_APP_BASE_URL}`,
|
||||
useEffect( () => {
|
||||
|
||||
apiRequest('/user-questionnaire/get-question-number', {
|
||||
params: {user_questionnaire_uuid: test.uuid},
|
||||
}
|
||||
)
|
||||
setCountQuestions(response.question_number)
|
||||
).then((res)=> setCountQuestions(res.question_number))
|
||||
|
||||
}, [])
|
||||
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="instruction">
|
||||
@ -36,7 +37,7 @@ export const Instruction = () => {
|
||||
e
|
||||
lit, sed do eiusmod tempo
|
||||
</div>
|
||||
<Link to="/quiz-test" className='instruction__btn quiz-btn quiz-btn_restriction'>Далее</Link>
|
||||
<Link to="/quiz/test" className='instruction__btn quiz-btn quiz-btn_restriction'>Далее</Link>
|
||||
<div className="instruction__info">
|
||||
<div className="instruction__icon">
|
||||
<img src={comment} alt=""/>
|
||||
@ -49,5 +50,5 @@ export const Instruction = () => {
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,7 @@ import {setSelectedTest} from "../../../redux/quizSlice";
|
||||
export const MyTestsQuiz = ({listTests}) => {
|
||||
|
||||
const formationEndingOfScore = (score) => {
|
||||
const lastNumber = score % 10
|
||||
const lastNumber = score % 10;
|
||||
if(score === 11 ||score === 12 ||score === 13 ||score === 14 ){
|
||||
return 'баллов'
|
||||
}else if(lastNumber === 2 || lastNumber === 3 || lastNumber === 4 ){
|
||||
@ -17,10 +17,10 @@ export const MyTestsQuiz = ({listTests}) => {
|
||||
}else{
|
||||
return 'баллов'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const recordSelectedTest = (item) => dispatch(setSelectedTest(item))
|
||||
const dispatch = useDispatch();
|
||||
const recordSelectedTest = (item) => dispatch(setSelectedTest(item));
|
||||
|
||||
return (
|
||||
<div className="my-tests">
|
||||
@ -35,10 +35,10 @@ export const MyTestsQuiz = ({listTests}) => {
|
||||
{item.questionnaire_title}
|
||||
</h3>
|
||||
<div className="item-test__body test-data">
|
||||
<Link to={'/quiz-interjacent'} className='quiz-btn'
|
||||
<Link to={'/quiz/interjacent'} className='quiz-btn'
|
||||
onClick={() => recordSelectedTest(item)}>Пройти</Link>
|
||||
</div>
|
||||
</article>
|
||||
</article>;
|
||||
case 2:
|
||||
return <article className="my-tests__item item-test" key={item.questionnaire_title}>
|
||||
<h3 className="item-test__name-test">
|
||||
@ -52,7 +52,7 @@ export const MyTestsQuiz = ({listTests}) => {
|
||||
<div className="test-data__hr"></div>
|
||||
<div className="test-data__score quiz-text">{item.score} {formationEndingOfScore(item.score)}</div>
|
||||
</div>
|
||||
</article>
|
||||
</article>;
|
||||
case 3:
|
||||
return <article className="my-tests__item item-test" key={item.questionnaire_title}>
|
||||
<h3 className="item-test__name-test">
|
||||
@ -61,7 +61,7 @@ export const MyTestsQuiz = ({listTests}) => {
|
||||
<div className="item-test__body test-data">
|
||||
<div className='quiz-btn'>На проверке</div>
|
||||
</div>
|
||||
</article>
|
||||
</article>;
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -70,4 +70,4 @@ export const MyTestsQuiz = ({listTests}) => {
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
};
|
@ -1,5 +1,4 @@
|
||||
import { Link } from 'react-router-dom'
|
||||
import avatar from './../../../images/medium_male.png'
|
||||
import React from 'react'
|
||||
import './quiz.scss'
|
||||
|
||||
export const Progressbar = ({indexQuestion, width}) => {
|
||||
@ -15,4 +14,4 @@ export const Progressbar = ({indexQuestion, width}) => {
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
};
|
@ -1,24 +1,22 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {fetchResultTest, selectedTest, selectResult} from "../../../redux/quizSlice";
|
||||
import {fetchGet} from "../../../server/server";
|
||||
import {useRequest} from "../../../hooks/useRequest";
|
||||
|
||||
|
||||
export const Results = () => {
|
||||
const result = useSelector(selectResult)
|
||||
const test = useSelector(selectedTest)
|
||||
const [maxScore, setMaxScore] = useState('')
|
||||
const dispatch = useDispatch()
|
||||
const result = useSelector(selectResult);
|
||||
const test = useSelector(selectedTest);
|
||||
const [maxScore, setMaxScore] = useState('');
|
||||
const dispatch = useDispatch();
|
||||
const {apiRequest} = useRequest();
|
||||
|
||||
useEffect(async () => {
|
||||
dispatch(fetchResultTest(test.uuid))
|
||||
const response = await fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/user-questionnaire/get-points-number?user_questionnaire_uuid=${test.uuid}`,
|
||||
Origin: `${process.env.REACT_APP_BASE_URL}`,
|
||||
}
|
||||
)
|
||||
setMaxScore(response.sum_point)
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
dispatch(fetchResultTest(test.uuid));
|
||||
apiRequest(`/user-questionnaire/get-points-number?user_questionnaire_uuid=${test.uuid}`)
|
||||
.then((res) => setMaxScore(res.sum_point));
|
||||
|
||||
}, [apiRequest, dispatch, test]);
|
||||
|
||||
return (
|
||||
<div className={'result _container'}>
|
||||
|
@ -1,59 +1,58 @@
|
||||
import React, {useEffect} from 'react'
|
||||
import {useHistory} from "react-router"
|
||||
import {CodeSnippetlighter} from '../../../pages/CodeSnippetPage'
|
||||
import './quiz.scss'
|
||||
import {useDispatch} from 'react-redux'
|
||||
import {useState} from 'react'
|
||||
import {
|
||||
fetchGetAnswers,
|
||||
selectAnswer,
|
||||
selectedTest
|
||||
} from '../../../redux/quizSlice'
|
||||
import {useSelector} from 'react-redux'
|
||||
import {Progressbar} from './ProgressbarQuiz'
|
||||
import {fetchUserAnswersMany, fetchUserAnswerOne} from './../../../redux/quizSlice'
|
||||
import {GetOptionTask} from './GetOptionTask'
|
||||
import {fetchGet} from "../../../server/server";
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {useNavigate} from "react-router-dom"
|
||||
import {useSelector, useDispatch} from 'react-redux'
|
||||
|
||||
import {useRequest} from "../../../hooks/useRequest";
|
||||
import {Progressbar} from './ProgressbarQuiz'
|
||||
import {GetOptionTask} from './GetOptionTask'
|
||||
|
||||
import {
|
||||
fetchUserAnswersMany, fetchUserAnswerOne, fetchGetAnswers, selectAnswer, selectedTest
|
||||
} from './../../../redux/quizSlice'
|
||||
|
||||
import './quiz.scss'
|
||||
|
||||
export const TaskQuiz = () => {
|
||||
|
||||
const history = useHistory();
|
||||
const dispatch = useDispatch()
|
||||
const listAnswers = useSelector(selectAnswer)
|
||||
const dataTest = useSelector(selectedTest)
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const listAnswers = useSelector(selectAnswer);
|
||||
const dataTest = useSelector(selectedTest);
|
||||
|
||||
const [index, setIndex] = useState(0);
|
||||
const [checkedValues, setCheckedValues] = useState([])
|
||||
const [checkedValues, setCheckedValues] = useState([]);
|
||||
const [stripValue, setStripValue] = useState(0);
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [questions, setQuestions] = useState([]);
|
||||
|
||||
const {apiRequest} = useRequest();
|
||||
|
||||
const id = localStorage.getItem('id');
|
||||
const [questions, setQuestions] = useState([])
|
||||
|
||||
useEffect(async () => {
|
||||
const response = await fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/question/get-questions?uuid=${dataTest.uuid}`,
|
||||
Origin: `${process.env.REACT_APP_BASE_URL}`,
|
||||
}
|
||||
)
|
||||
setQuestions(response)
|
||||
dispatch(fetchGetAnswers(response[0].id))
|
||||
useEffect(() => {
|
||||
apiRequest(`/question/get-questions?uuid=${dataTest.uuid}`)
|
||||
.then((response) => {
|
||||
console.log(response)
|
||||
setQuestions(response);
|
||||
dispatch(fetchGetAnswers(response[0].id));
|
||||
setStripValue((+index + 1) * 100 / response.length)
|
||||
}, [dispatch])
|
||||
|
||||
})
|
||||
}, [dispatch]);
|
||||
|
||||
const nextQuestion = async (e) => {
|
||||
e.preventDefault()
|
||||
e.preventDefault();
|
||||
|
||||
//Проверка на валидацию ответов
|
||||
if (checkedValues.length || inputValue) {
|
||||
switch (questions[index].question_type_id) {
|
||||
case '3':
|
||||
dispatch(fetchUserAnswersMany(checkedValues))
|
||||
dispatch(fetchUserAnswersMany(checkedValues));
|
||||
break;
|
||||
case '2':
|
||||
case '1':
|
||||
case '4':
|
||||
dispatch(fetchUserAnswerOne(checkedValues))
|
||||
dispatch(fetchUserAnswerOne(checkedValues));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -61,20 +60,20 @@ export const TaskQuiz = () => {
|
||||
|
||||
//Проверка на существование следующего запроса
|
||||
if (index < questions.length - 1) {
|
||||
await dispatch(fetchGetAnswers(questions[index + 1].id))
|
||||
setIndex(prev => prev >= questions.length - 1 ? prev : prev + 1)
|
||||
setStripValue((prev => prev + (100 / questions.length)))
|
||||
await dispatch(fetchGetAnswers(questions[index + 1].id));
|
||||
setIndex(prev => prev >= questions.length - 1 ? prev : prev + 1);
|
||||
setStripValue((prev => prev + (100 / questions.length)));
|
||||
setCheckedValues([]);
|
||||
setInputValue('')
|
||||
} else {
|
||||
history.push(`/quiz-result`)
|
||||
navigate(`/quiz/result`);
|
||||
alert("Тест пройден!")
|
||||
}
|
||||
|
||||
} else {
|
||||
alert("Вы не ответили на вопрос")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
const checked = e.target.checked;
|
||||
@ -86,8 +85,8 @@ export const TaskQuiz = () => {
|
||||
question_id: questions[index].id,
|
||||
response_body: e.target.value
|
||||
}]) :
|
||||
setCheckedValues(prev => [...prev.filter(item => item.response_body !== e.target.value)])
|
||||
break
|
||||
setCheckedValues(prev => [...prev.filter(item => item.response_body !== e.target.value)]);
|
||||
break;
|
||||
case '1':
|
||||
case '2':
|
||||
case '4':
|
||||
@ -136,7 +135,8 @@ export const TaskQuiz = () => {
|
||||
{questions.length !== index + 1 &&
|
||||
<button type='submit' className='quiz-btn'
|
||||
onClick={(e) => nextQuestion(e)}>Далее</button>}
|
||||
{questions.length === index + 1 && <button onClick={(e) => nextQuestion(e)}
|
||||
{questions.length === index + 1 &&
|
||||
<button onClick={(e) => nextQuestion(e)}
|
||||
className='quiz-btn quiz-btn_dark-green'>Завершить</button>}
|
||||
</div>
|
||||
</form>
|
||||
@ -146,4 +146,4 @@ export const TaskQuiz = () => {
|
||||
</div>
|
||||
</React.StrictMode>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
BIN
src/fonts/LabGrotesque-Bold.eot
Normal file
BIN
src/fonts/LabGrotesque-Bold.ttf
Normal file
BIN
src/fonts/LabGrotesque-Bold.woff
Normal file
BIN
src/fonts/LabGrotesque-Bold.woff2
Normal file
BIN
src/fonts/LabGrotesque-Light.eot
Normal file
BIN
src/fonts/LabGrotesque-Light.ttf
Normal file
BIN
src/fonts/LabGrotesque-Light.woff
Normal file
BIN
src/fonts/LabGrotesque-Light.woff2
Normal file
BIN
src/fonts/LabGrotesque-Medium.eot
Normal file
BIN
src/fonts/LabGrotesque-Medium.ttf
Normal file
BIN
src/fonts/LabGrotesque-Medium.woff
Normal file
BIN
src/fonts/LabGrotesque-Medium.woff2
Normal file
BIN
src/fonts/LabGrotesque-Regular.eot
Normal file
BIN
src/fonts/LabGrotesque-Regular.ttf
Normal file
BIN
src/fonts/LabGrotesque-Regular.woff
Normal file
BIN
src/fonts/LabGrotesque-Regular.woff2
Normal file
@ -353,3 +353,44 @@
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'LabGrotesque';
|
||||
src: url('LabGrotesque-Regular.eot');
|
||||
src: local('LabGrotesque-Regular'), local('LabGrotesque-Regular'),
|
||||
url('LabGrotesque-Regular.eot?#iefix') format('embedded-opentype'),
|
||||
url('LabGrotesque-Regular.woff2') format('woff2'), url('LabGrotesque-Regular.woff') format('woff'),
|
||||
url('LabGrotesque-Regular.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'LabGrotesque';
|
||||
src: url('LabGrotesque-Medium.eot');
|
||||
src: local('LabGrotesque-Medium'), local('LabGrotesque-Medium'),
|
||||
url('LabGrotesque-Medium.eot?#iefix') format('embedded-opentype'),
|
||||
url('LabGrotesque-Medium.woff2') format('woff2'), url('LabGrotesque-Medium.woff') format('woff'),
|
||||
url('LabGrotesque-Medium.ttf') format('truetype');
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'LabGrotesque';
|
||||
src: url('LabGrotesque-Bold.eot');
|
||||
src: local('LabGrotesque-Bold'), local('LabGrotesque-Bold'),
|
||||
url('LabGrotesque-Bold.eot?#iefix') format('embedded-opentype'),
|
||||
url('LabGrotesque-Bold.woff2') format('woff2'), url('LabGrotesque-Bold.woff') format('woff'),
|
||||
url('LabGrotesque-Bold.ttf') format('truetype');
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'LabGrotesque';
|
||||
src: url('LabGrotesque-Light.eot');
|
||||
src: local('LabGrotesque-Light'), local('LabGrotesque-Light'),
|
||||
url('LabGrotesque-Light.eot?#iefix') format('embedded-opentype'),
|
||||
url('LabGrotesque-Light.woff2') format('woff2'), url('LabGrotesque-Light.woff') format('woff'),
|
||||
url('LabGrotesque-Light.ttf') format('truetype');
|
||||
font-weight: 300;
|
||||
}
|
||||
|
37
src/helper.js
Normal file
@ -0,0 +1,37 @@
|
||||
export function createMarkup(text) {
|
||||
return {__html: text.split('</p>').join('</p>')}
|
||||
}
|
||||
|
||||
export function transformHtml(text) {
|
||||
let startHtml = {__html: text.split('<h2>').join('<br><h2>').split('<br>')};
|
||||
startHtml = startHtml.__html.filter((el) =>
|
||||
el !== null && el !== "" || el === 0
|
||||
);
|
||||
const finalHtml = startHtml.map((item) =>
|
||||
`<div class='experience__block'>
|
||||
<div class="summary__sections__head">
|
||||
<h3>Описание опыта работы</h3>
|
||||
<button>Редактировать раздел</button>
|
||||
</div>
|
||||
<div class="experience__content">${item.split('<h3>')[0]}</div>
|
||||
</div>`
|
||||
);
|
||||
return {__html: finalHtml.join('')}
|
||||
}
|
||||
//
|
||||
// export const setToken = () => {
|
||||
// const url = new URL(window.location.href);
|
||||
// const urlT = url.searchParams.get("token");
|
||||
// urlT ? sessionStorage.setItem('token', 'Bearer ' + urlT) : '';
|
||||
// const tParam = urlT || sessionStorage.getItem('token');
|
||||
// return tParam ? {"Authorization": tParam} : false
|
||||
//
|
||||
// };
|
||||
|
||||
export const getToken = () => {
|
||||
const tParam = `Bearer ${localStorage.getItem('auth_token')}`
|
||||
|
||||
return tParam ? {Authorization: tParam} : {};
|
||||
};
|
||||
|
||||
export const urlHasParams = (url) => url.indexOf('?') > 0 ? `${url}&${window.location.search.substr(1)}` : `${url}${window.location.search}`;
|
18
src/hooks/useLogout.js
Normal file
@ -0,0 +1,18 @@
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {getRole} from "../redux/roleSlice";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
import {auth} from "../redux/outstaffingSlice";
|
||||
|
||||
export const useLogout = () => {
|
||||
const dispatch = useDispatch();
|
||||
const userRole = useSelector(getRole);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const logout = () => {
|
||||
localStorage.clear();
|
||||
dispatch(auth(false));
|
||||
navigate(userRole === 'ROLE_DEV' ? '/authdev' : '/auth')
|
||||
};
|
||||
|
||||
return {logout}
|
||||
};
|
61
src/hooks/useRequest.js
Normal file
@ -0,0 +1,61 @@
|
||||
import axios from 'axios';
|
||||
import {getToken, urlHasParams} from "../helper";
|
||||
import {useLogout} from "./useLogout";
|
||||
|
||||
|
||||
const instance = axios.create({
|
||||
baseURL: process.env.REACT_APP_API_URL,
|
||||
validateStatus(status) {
|
||||
return status;
|
||||
},
|
||||
});
|
||||
|
||||
export const useRequest = () => {
|
||||
|
||||
|
||||
const {logout} = useLogout();
|
||||
|
||||
const apiRequest = (url, {
|
||||
method = 'get', params, data,
|
||||
headers = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
} = {}) => {
|
||||
const fullHeaders = {...headers, ...getToken()};
|
||||
let urWithParams = urlHasParams(url);
|
||||
|
||||
|
||||
return instance
|
||||
.request({
|
||||
url: urWithParams,
|
||||
method,
|
||||
params,
|
||||
data,
|
||||
headers: {...fullHeaders},
|
||||
})
|
||||
.then(response => new Promise(resolve => {
|
||||
if (response.data.redirect || response.status === 401) {
|
||||
logout()
|
||||
}
|
||||
return resolve(response)
|
||||
}))
|
||||
.then(response => new Promise(resolve => resolve(response.data)))
|
||||
};
|
||||
|
||||
const RequestError = (code, msg, data) => {
|
||||
const description = msg ? `- ${msg}` : '';
|
||||
|
||||
this.name = 'RequestError';
|
||||
this.message = `API returned: ${code}${description}.`;
|
||||
this.code = code;
|
||||
this.description = msg;
|
||||
this.data = data;
|
||||
};
|
||||
|
||||
RequestError.prototype = Object.create(Error.prototype);
|
||||
RequestError.prototype.constructor = RequestError;
|
||||
|
||||
|
||||
return {apiRequest}
|
||||
};
|
BIN
src/images/arrowRight.png
Normal file
After Width: | Height: | Size: 240 B |
BIN
src/images/gitItemImg.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
src/images/paymentIcon.png
Normal file
After Width: | Height: | Size: 966 B |
BIN
src/images/reports.png
Normal file
After Width: | Height: | Size: 845 B |
BIN
src/images/settingIcon.png
Normal file
After Width: | Height: | Size: 836 B |
BIN
src/images/summaryIcon.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
src/images/timerIcon.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
|
||||
import { AuthBox } from '../AuthBox/AuthBox'
|
||||
import { AuthBox } from '../../components/AuthBox/AuthBox'
|
||||
|
||||
import { useSelector } from 'react-redux'
|
||||
import arrow from '../../images/arrow__login_page.png'
|
||||
@ -10,16 +10,18 @@ import text from '../../images/Body_Text.png'
|
||||
import vector from '../../images/Vector_Smart_Object.png'
|
||||
|
||||
import { selectAuth } from '../../redux/outstaffingSlice'
|
||||
import { Redirect } from 'react-router-dom'
|
||||
import { Footer } from '../Footer/Footer'
|
||||
import { useNavigate} from 'react-router-dom'
|
||||
import { Footer } from '../../components/Footer/Footer'
|
||||
|
||||
import './authForDevelopers.scss'
|
||||
|
||||
const AuthForDevelopers = () => {
|
||||
const isAuth = useSelector(selectAuth)
|
||||
|
||||
const isAuth = useSelector(selectAuth);
|
||||
let navigate = useNavigate();
|
||||
|
||||
if (isAuth) {
|
||||
return <Redirect to='/report' />
|
||||
navigate('/profile')
|
||||
}
|
||||
|
||||
return (
|
||||
@ -89,6 +91,6 @@ const AuthForDevelopers = () => {
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default AuthForDevelopers
|
@ -7,18 +7,19 @@ import vector from '../../images/Vector_Smart_Object.png'
|
||||
import vectorBlack from '../../images/Vector_Smart_Object_black.png'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { selectAuth } from '../../redux/outstaffingSlice'
|
||||
import { Redirect } from 'react-router-dom'
|
||||
import { useNavigate} from 'react-router-dom'
|
||||
|
||||
import { Footer } from '../Footer/Footer'
|
||||
import { AuthBox } from '../AuthBox/AuthBox'
|
||||
import { Footer } from '../../components/Footer/Footer'
|
||||
import { AuthBox } from '../../components/AuthBox/AuthBox'
|
||||
|
||||
import './authForPartners.scss'
|
||||
|
||||
const AuthForPartners = () => {
|
||||
const isAuth = useSelector(selectAuth)
|
||||
const isAuth = useSelector(selectAuth);
|
||||
let navigate = useNavigate();
|
||||
|
||||
if (isAuth) {
|
||||
return <Redirect to='/' />
|
||||
navigate('/')
|
||||
}
|
||||
|
||||
return (
|
||||
@ -81,6 +82,6 @@ const AuthForPartners = () => {
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default AuthForPartners
|
@ -1,8 +0,0 @@
|
||||
import React from 'react';
|
||||
import AuthForDevelopers from '../components/Auth/AuthForDevelopers';
|
||||
|
||||
const AuthPageForDevelopers = () => {
|
||||
return <AuthForDevelopers />;
|
||||
};
|
||||
|
||||
export default AuthPageForDevelopers;
|
@ -1,8 +0,0 @@
|
||||
import React from 'react';
|
||||
import AuthForPartners from '../components/Auth/AuthForPartners';
|
||||
|
||||
const AuthPageForPartners = () => {
|
||||
return <AuthForPartners />;
|
||||
};
|
||||
|
||||
export default AuthPageForPartners;
|
@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { WithLogout } from '../hoc/withLogout';
|
||||
import Calendar from '../components/Calendar/Calendar';
|
||||
|
||||
const CalendarPage = () => {
|
||||
const history = useHistory();
|
||||
return <WithLogout><Calendar onSelect={() => { history.push('/report/0') }} /></WithLogout>;
|
||||
const navigate = useNavigate();
|
||||
return <WithLogout><Calendar onSelect={() => { navigate('/report/0') }} /></WithLogout>;
|
||||
};
|
||||
|
||||
export default CalendarPage;
|
||||
|
@ -1,53 +1,51 @@
|
||||
import React, { useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { useHistory, useParams, Link } from 'react-router-dom'
|
||||
import React from 'react'
|
||||
import {useDispatch, useSelector} from 'react-redux'
|
||||
import {useParams, useNavigate} from 'react-router-dom'
|
||||
import {
|
||||
currentCandidate,
|
||||
selectCurrentCandidate,
|
||||
auth
|
||||
} from '../redux/outstaffingSlice'
|
||||
import SVG from 'react-inlinesvg'
|
||||
import { WithLogout } from '../hoc/withLogout'
|
||||
import {WithLogout} from '../hoc/withLogout'
|
||||
import Form from '../components/Form/Form'
|
||||
import { LEVELS, SKILLS } from '../components/constants/constants'
|
||||
import { fetchGet } from '../server/server'
|
||||
import { Footer } from '../components/Footer/Footer'
|
||||
import {LEVELS, SKILLS} from '../constants/constants'
|
||||
import {Footer} from '../components/Footer/Footer'
|
||||
|
||||
import arrow from '../images/right-arrow.png'
|
||||
import rectangle from '../images/rectangle_secondPage.png'
|
||||
import telegramIcon from '../images/telegram-icon.svg'
|
||||
|
||||
import './formPage.scss'
|
||||
import { getRole } from '../redux/roleSlice'
|
||||
import {useRequest} from "../hooks/useRequest";
|
||||
|
||||
const goBack = (history) => {
|
||||
history.goBack()
|
||||
}
|
||||
|
||||
const FormPage = () => {
|
||||
const params = useParams()
|
||||
const history = useHistory()
|
||||
const dispatch = useDispatch()
|
||||
const candidate = useSelector(selectCurrentCandidate)
|
||||
const role = useSelector(getRole)
|
||||
const params = useParams();
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const candidate = useSelector(selectCurrentCandidate);
|
||||
|
||||
|
||||
const {apiRequest} = useRequest();
|
||||
|
||||
const goBack = () => {
|
||||
navigate(-1)
|
||||
};
|
||||
|
||||
if (!candidate.id) {
|
||||
fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/profile/`,
|
||||
params: Number(params.id),
|
||||
history,
|
||||
role,
|
||||
logout: () => dispatch(auth(false))
|
||||
}).then((el) => dispatch(currentCandidate(el)))
|
||||
apiRequest('/profile', {
|
||||
params: Number(params.id)
|
||||
})
|
||||
.then((el) => dispatch(currentCandidate(el)))
|
||||
}
|
||||
|
||||
return (
|
||||
<WithLogout>
|
||||
<div className='form-page'>
|
||||
<div className='form-page__back'>
|
||||
<div className='form-page__arrow' onClick={() => goBack(history)}>
|
||||
<div className='form-page__arrow' onClick={goBack}>
|
||||
<div className='form-page__arrow-img'>
|
||||
<img src={arrow} alt='' />
|
||||
<img src={arrow} alt=''/>
|
||||
</div>
|
||||
<div className='form-page__back-to-candidate'>
|
||||
<span>Вернуться к кандидату</span>
|
||||
@ -56,7 +54,7 @@ const FormPage = () => {
|
||||
</div>
|
||||
<div className='form-page__candidate'>
|
||||
<div className='form-page__avatar'>
|
||||
<img src={candidate.photo} />
|
||||
<img src={candidate.photo} alt='candidate avatar'/>
|
||||
</div>
|
||||
<div className='form-page__candidate-info'>
|
||||
<div className='form-page__position'>
|
||||
@ -66,14 +64,14 @@ const FormPage = () => {
|
||||
</span>
|
||||
</div>
|
||||
<div className='form-page__selected'>
|
||||
<img src={rectangle} />
|
||||
<img src={rectangle} alt='rectangle'/>
|
||||
<span>Выбранный кандидат</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='form-page__interview'>
|
||||
<div className='form-page__form'>
|
||||
<Form />
|
||||
<Form/>
|
||||
</div>
|
||||
<div className='form-page__separator'>
|
||||
<div className='form-page__line'></div>
|
||||
@ -84,16 +82,16 @@ const FormPage = () => {
|
||||
Заявка на собеседование через телеграм
|
||||
</div>
|
||||
<div className='form-page__telegram-icon'>
|
||||
<a href='https://t.me/st0kir' target='_blank'>
|
||||
<SVG src={telegramIcon} />
|
||||
<a href='https://t.me/st0kir' target='_blank' rel="noreferrer">
|
||||
<SVG src={telegramIcon}/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
<Footer/>
|
||||
</div>
|
||||
</WithLogout>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default FormPage
|
||||
|
100
src/pages/Profile/Profile.js
Normal file
@ -0,0 +1,100 @@
|
||||
import React from 'react';
|
||||
import {useSelector} from "react-redux";
|
||||
import {Link} from "react-router-dom";
|
||||
|
||||
import {ProfileHeader} from "../../components/ProfileHeader/ProfileHeader";
|
||||
import {Footer} from "../../components/Footer/Footer";
|
||||
|
||||
import {getProfileInfo} from "../../redux/outstaffingSlice";
|
||||
|
||||
import reportsIcon from "../../images/reports.png"
|
||||
import summaryIcon from "../../images/summaryIcon.png"
|
||||
import timerIcon from "../../images/timerIcon.png"
|
||||
import paymentIcon from "../../images/paymentIcon.png"
|
||||
import settingIcon from "../../images/settingIcon.png"
|
||||
import rightArrow from "../../images/arrowRight.png"
|
||||
|
||||
import './profile.scss'
|
||||
|
||||
export const Profile = () => {
|
||||
|
||||
const profileInfo = useSelector(getProfileInfo);
|
||||
|
||||
return (
|
||||
<div className='profile'>
|
||||
<ProfileHeader/>
|
||||
<div className='container'>
|
||||
<h2 className='profile__title'>Добрый день, <span>{profileInfo.fio}</span></h2>
|
||||
<div className='summary__info'>
|
||||
<div className='summary__person'>
|
||||
<img src={profileInfo.photo} className='summary__avatar' alt='avatar'/>
|
||||
<p className='summary__name'>{profileInfo.fio} {profileInfo.specification}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='profile__items'>
|
||||
<Link to={'/profile/calendar'} className='item'>
|
||||
<div className='item__about'>
|
||||
<img src={reportsIcon} alt='report'/>
|
||||
<h3>Ваша отчетность</h3>
|
||||
</div>
|
||||
<div className='item__info'>
|
||||
<p><span></span>Отработанных в этом месяце часов</p>
|
||||
<div className='item__infoLink'>
|
||||
<img src={rightArrow} alt='arrow'/>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<Link to={'/profile/summary'} className='item'>
|
||||
<div className='item__about'>
|
||||
<img src={summaryIcon} alt='summary'/>
|
||||
<h3>Данные и резюме</h3>
|
||||
</div>
|
||||
<div className='item__info'>
|
||||
<p>Ваше резюме<br/><span>заполнено</span></p>
|
||||
<div className='item__infoLink'>
|
||||
<img src={rightArrow} alt='arrow'/>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<Link to={'/profile'} className='item'>
|
||||
<div className='item__about'>
|
||||
<img src={timerIcon} alt='timer'/>
|
||||
<h3>Трекер времени</h3>
|
||||
</div>
|
||||
<div className='item__info'>
|
||||
<p>Сколько времени занимает<br/> выполнение задач</p>
|
||||
<div className='item__infoLink'>
|
||||
<img src={rightArrow} alt='arrow'/>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<Link to={'/profile'} className='item'>
|
||||
<div className='item__about'>
|
||||
<img src={paymentIcon} alt='payment'/>
|
||||
<h3>Выплаты</h3>
|
||||
</div>
|
||||
<div className='item__info'>
|
||||
<p>У вас <span>подтвержден</span><br/> статус самозанятого</p>
|
||||
<div className='item__infoLink'>
|
||||
<img src={rightArrow} alt='arrow'/>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<Link to={'/profile'} className='item'>
|
||||
<div className='item__about'>
|
||||
<img src={settingIcon} alt='settings'/>
|
||||
<h3>Настройки аккаунта</h3>
|
||||
</div>
|
||||
<div className='item__info'>
|
||||
<p>Перейдите чтобы начать<br/> редактирование</p>
|
||||
<div className='item__infoLink'>
|
||||
<img src={rightArrow} alt='arrow'/>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<Footer/>
|
||||
</div>
|
||||
)
|
||||
};
|
126
src/pages/Profile/profile.scss
Normal file
@ -0,0 +1,126 @@
|
||||
.profile {
|
||||
background: #F1F1F1;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
font-family: 'LabGrotesque', sans-serif;
|
||||
|
||||
&__title {
|
||||
font-weight: 700;
|
||||
font-size: 22px;
|
||||
line-height: 32px;
|
||||
color: #000000;
|
||||
span {
|
||||
color: #52B709;
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
font-size: 17px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
min-height: 128px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 130px 0 45px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&__items {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 30px;
|
||||
row-gap: 25px;
|
||||
column-gap: 35px;
|
||||
|
||||
@media (max-width: 1175px) {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.item {
|
||||
max-width: 353px;
|
||||
width: 100%;
|
||||
padding: 35px 45px 15px 30px;
|
||||
background: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 6px 5px 20px rgb(87 98 80 / 21%);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
@media (max-width: 1175px) {
|
||||
width: 48%;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
@media (max-width: 925px) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__about {
|
||||
display: flex;
|
||||
column-gap: 20px;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
|
||||
h3 {
|
||||
color: #000000;
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
max-width: 125px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
p {
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
color: #000000;
|
||||
margin-bottom: 0;
|
||||
|
||||
span {
|
||||
color: #52B709;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
&Link {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: #DDEEC6;
|
||||
border-radius: 50px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1160px;
|
||||
margin-top: 23px;
|
||||
|
||||
@media (max-width: 570px) {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 70px;
|
||||
}
|
||||
}
|
8
src/pages/ProfileCalendarPage.js
Normal file
@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
import { ProfileCalendar } from '../../src/components/ProfileCalendar/ProfileCalendar';
|
||||
|
||||
const ProfileCalendarPage = () => {
|
||||
return <ProfileCalendar/>;
|
||||
};
|
||||
|
||||
export default ProfileCalendarPage;
|
@ -2,6 +2,6 @@ import React from 'react';
|
||||
import { WithLogout } from '../hoc/withLogout';
|
||||
import ReportForm from '../components/ReportForm/ReportForm';
|
||||
|
||||
const ReportFormPage = () => <WithLogout><ReportForm /></WithLogout>;
|
||||
const ReportFormPage = () => <ReportForm />;
|
||||
|
||||
export default ReportFormPage;
|
||||
|
@ -22,7 +22,7 @@ const tasks = [
|
||||
text: 'Задача «83 – Навигационная система – Поиск по почтовому индексу – Добавить экран поиска по почтовому индексу» не может быть завершена, т.к. работа над задачей «82 – Навигационная система – Разработать модуль поиска по почтовому индексу» ещё не начата',
|
||||
hours: 3
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
const SingleReportPage = () => {
|
||||
return (
|
||||
@ -30,7 +30,7 @@ const SingleReportPage = () => {
|
||||
<div className='single-report-page'>
|
||||
<div className='single-report-page__back'>
|
||||
<div className='single-report-page__back-arrow'>
|
||||
<img src={arrowLeft} />
|
||||
<img src={arrowLeft} alt='arrowLeft'/>
|
||||
</div>
|
||||
<div className='single-report-page__back-text'>
|
||||
Вернуться к списку
|
||||
@ -103,6 +103,6 @@ const SingleReportPage = () => {
|
||||
</div>
|
||||
</WithLogout>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default SingleReportPage
|
||||
|
101
src/pages/Summary/Summary.js
Normal file
@ -0,0 +1,101 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {useSelector} from "react-redux";
|
||||
import {ProfileHeader} from "../../components/ProfileHeader/ProfileHeader";
|
||||
import {getProfileInfo} from "../../redux/outstaffingSlice";
|
||||
import {Footer} from '../../components/Footer/Footer'
|
||||
import {transformHtml} from "../../helper";
|
||||
|
||||
import {useRequest} from "../../hooks/useRequest";
|
||||
|
||||
import arrow from "../../images/right-arrow.png";
|
||||
import rightArrow from "../../images/arrowRight.png"
|
||||
import gitImgItem from "../../images/gitItemImg.png"
|
||||
|
||||
import './summary.scss'
|
||||
|
||||
export const Summary = () => {
|
||||
const profileInfo = useSelector(getProfileInfo);
|
||||
const [openGit, setOpenGit] = useState(false);
|
||||
const [gitInfo, setGitInfo] = useState([]);
|
||||
|
||||
const {apiRequest} = useRequest();
|
||||
useEffect(() => {
|
||||
apiRequest(`/profile/portfolio-projects?card_id=${localStorage.getItem('cardId')}`)
|
||||
.then(responseGit => setGitInfo(responseGit))
|
||||
}, []);
|
||||
return (
|
||||
<div className='summary'>
|
||||
<ProfileHeader/>
|
||||
<div className='container'>
|
||||
<div className='summary__content'>
|
||||
<h2 className='summary__title'>Ваше резюме {openGit && <span>- Git</span>}</h2>
|
||||
{openGit && <div className='summary__back' onClick={() => setOpenGit(false)}>
|
||||
<img src={arrow} alt='arrow'/>
|
||||
<p>Вернуться</p>
|
||||
</div>}
|
||||
<div className='summary__info'>
|
||||
<div className='summary__person'>
|
||||
<img src={profileInfo.photo} className='summary__avatar' alt='avatar'/>
|
||||
<p className='summary__name'>{profileInfo.fio} {profileInfo.specification}</p>
|
||||
</div>
|
||||
{!openGit &&
|
||||
<button className='summary__git' onClick={() => setOpenGit(true)}>Git</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{!openGit &&
|
||||
<div className='summary__skills skills__section'>
|
||||
<div className='summary__sections__head'>
|
||||
<h3>Основной стек</h3>
|
||||
<button>Редактировать раздел</button>
|
||||
</div>
|
||||
<div className='skills__section__items'>
|
||||
<div className='skills__section__items__wrapper'>
|
||||
{profileInfo.skillValues && profileInfo.skillValues.map((skill) =>
|
||||
<span key={skill.id} className='skill_item'>{skill.skill.name}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{profileInfo.vc_text && !openGit &&
|
||||
<div className='summary__experience' dangerouslySetInnerHTML={transformHtml(profileInfo.vc_text)}>
|
||||
</div>
|
||||
}
|
||||
{openGit &&
|
||||
<div className='summary__sectionGit'>
|
||||
<div className='summary__sections__head'>
|
||||
<h3>Страница портфолио кода разработчика</h3>
|
||||
<button>Редактировать раздел</button>
|
||||
</div>
|
||||
<div className='summary__sectionGitItems'>
|
||||
{gitInfo.length && gitInfo.map((itemGit) => {
|
||||
return <div key={itemGit.id} className='summary__sectionGitItem gitItem'>
|
||||
<div className='gitItem__info'>
|
||||
<div className='gitItem__info__about'>
|
||||
<img src={gitImgItem} alt='gitImg'/>
|
||||
<div className='gitItem__info__name'>
|
||||
<h4>{itemGit.title}</h4>
|
||||
<p>{itemGit.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='gitItem__info__specification'>
|
||||
<span className='gitItem__lineSkill'/>
|
||||
<p>{itemGit.main_stack}</p>
|
||||
</div>
|
||||
</div>
|
||||
<a className='gitItem__link' href={itemGit.link} target="_blank" rel="noreferrer">
|
||||
<img src={rightArrow} alt='arrowRight'/>
|
||||
</a>
|
||||
</div>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<Footer/>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
391
src/pages/Summary/summary.scss
Normal file
@ -0,0 +1,391 @@
|
||||
.summary {
|
||||
background: #F1F1F1;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
font-family: 'LabGrotesque', sans-serif;
|
||||
//
|
||||
//&__container {
|
||||
// max-width: 1160px;
|
||||
// padding: 0 10px;
|
||||
// margin: 20px auto;
|
||||
// position: relative;
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
//}
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-weight: 700;
|
||||
font-size: 22px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 0;
|
||||
|
||||
span {
|
||||
color: #52B709;
|
||||
}
|
||||
}
|
||||
|
||||
&__back {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 30px;
|
||||
margin-top: 20px;
|
||||
cursor: pointer;
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
min-height: 128px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 130px 0 45px;
|
||||
justify-content: space-between;
|
||||
|
||||
@media (max-width: 930px) {
|
||||
padding: 0 40px;
|
||||
}
|
||||
|
||||
@media (max-width: 690px) {
|
||||
padding: 0 20px;
|
||||
min-height: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
&__person {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 45px;
|
||||
|
||||
@media (max-width: 690px) {
|
||||
column-gap: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
border-radius: 100px;
|
||||
|
||||
@media (max-width: 690px) {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
&__name {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
position: relative;
|
||||
|
||||
@media (max-width: 690px) {
|
||||
font-size: 14px;
|
||||
margin-right: 10px;
|
||||
line-height: 15px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background: #52B709;
|
||||
border-radius: 12px;
|
||||
width: 70%;
|
||||
height: 8px;
|
||||
bottom: -14px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__git {
|
||||
background: #52B709;
|
||||
border-radius: 44px;
|
||||
width: 177px;
|
||||
height: 50px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
color: white;
|
||||
border: none;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 6px 5px 20px rgb(87 98 80 / 21%);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
@media (max-width: 690px) {
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
&__skills {
|
||||
background: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
margin-top: 35px;
|
||||
}
|
||||
|
||||
&__sections__head {
|
||||
display: flex;
|
||||
min-height: 69px;
|
||||
background: #E1FCCF;
|
||||
border-radius: 12px 12px 0px 0px;
|
||||
align-items: center;
|
||||
padding: 0 35px 0 50px;
|
||||
justify-content: space-between;
|
||||
|
||||
@media (max-width: 550px) {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-style: normal;
|
||||
font-size: 18px;
|
||||
line-height: 32px;
|
||||
|
||||
@media (max-width: 660px) {
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
background: #FFFFFF;
|
||||
border-radius: 44px;
|
||||
padding: 10px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: none;
|
||||
height: 42px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
white-space: nowrap;
|
||||
|
||||
@media (max-width: 520px) {
|
||||
font-size: 12px;
|
||||
padding: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.skills__section {
|
||||
&__items {
|
||||
padding: 25px 35px 25px 50px;
|
||||
|
||||
@media (max-width: 550px) {
|
||||
padding: 15px 15px 20px;
|
||||
}
|
||||
|
||||
&__wrapper {
|
||||
max-width: 630px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 5px;
|
||||
|
||||
.skill_item {
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
white-space: nowrap;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__experience {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 20px;
|
||||
margin-top: 30px;
|
||||
|
||||
.experience {
|
||||
&__block {
|
||||
background: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding: 15px 35px 15px 50px;
|
||||
|
||||
@media (max-width: 550px) {
|
||||
padding: 15px 15px 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__sectionGit {
|
||||
margin-top: 25px;
|
||||
|
||||
&Items {
|
||||
margin-top: 25px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
row-gap: 20px;
|
||||
column-gap: 25px;
|
||||
justify-content: space-between;
|
||||
|
||||
.gitItem {
|
||||
width: 48%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
padding: 35px 30px 30px 45px;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 6px 5px 20px rgb(87 98 80 / 21%);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
@media (max-width: 825px) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 470px) {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 350px;
|
||||
width: 100%;
|
||||
|
||||
@media (max-width: 825px) {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&__about {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 15px;
|
||||
}
|
||||
|
||||
&__name {
|
||||
h4 {
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 300;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 0;
|
||||
white-space: nowrap;
|
||||
max-width: 300px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@media (max-width: 1040px) {
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
@media (max-width: 890px) {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
@media (max-width: 825px) {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
@media (max-width: 470px) {
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__specification {
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 10px;
|
||||
column-gap: 25px;
|
||||
|
||||
@media (max-width: 470px) {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
background: #D4F123;
|
||||
border-radius: 12px;
|
||||
max-width: 260px;
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__link {
|
||||
border-radius: 50%;
|
||||
background: #DDEEC6;
|
||||
min-width: 48px;
|
||||
height: 48px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 70px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1160px;
|
||||
margin-top: 23px;
|
||||
|
||||
@media (max-width: 570px) {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import {Redirect} from "react-router-dom"
|
||||
import { HeaderPageTestsQuiz } from "../../components/features/quiz/HeaderPageTests"
|
||||
import { Instruction } from "../../components/features/quiz/Instructions"
|
||||
import {useNavigate} from "react-router-dom"
|
||||
import {HeaderPageTestsQuiz} from "../../components/features/quiz/HeaderPageTests"
|
||||
import {Instruction} from "../../components/features/quiz/Instructions"
|
||||
import React from "react";
|
||||
import {useSelector} from "react-redux";
|
||||
import {selectedTest} from "../../redux/quizSlice";
|
||||
@ -10,14 +10,15 @@ export const InstructionPage = () => {
|
||||
|
||||
const test = useSelector(selectedTest)
|
||||
|
||||
if(!test){
|
||||
return <Redirect to={'/quiz'} />
|
||||
let navigate = useNavigate();
|
||||
if (!test) {
|
||||
navigate('/quiz')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderPageTestsQuiz isVisibilityButton={false}/>
|
||||
<Instruction />
|
||||
<Instruction/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
};
|
@ -1,18 +1,20 @@
|
||||
import {Redirect} from "react-router-dom"
|
||||
import React from "react";
|
||||
import {useNavigate} from "react-router-dom"
|
||||
import {useSelector} from "react-redux";
|
||||
|
||||
import {HeaderPageTestsQuiz} from "../../components/features/quiz/HeaderPageTests"
|
||||
import {MyTestsQuiz} from "../../components/features/quiz/MyTestsQuiz"
|
||||
import {useSelector} from "react-redux";
|
||||
|
||||
import {selectedTest, selectPassedTests} from "../../redux/quizSlice";
|
||||
import React from "react";
|
||||
|
||||
|
||||
export const InterjacentPage = () => {
|
||||
|
||||
const test = useSelector(selectedTest)
|
||||
const test = useSelector(selectedTest);
|
||||
const passedTests = useSelector(selectPassedTests)
|
||||
|
||||
let navigate = useNavigate();
|
||||
if (!test) {
|
||||
return <Redirect to={'/quiz'}/>
|
||||
navigate('/quiz')
|
||||
}
|
||||
|
||||
return (
|
||||
@ -21,4 +23,4 @@ export const InterjacentPage = () => {
|
||||
<MyTestsQuiz listTests={passedTests}/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
};
|
@ -1,17 +1,16 @@
|
||||
import {Link, Redirect} from 'react-router-dom'
|
||||
import {useNavigate} from 'react-router-dom'
|
||||
import {HeaderPageTestsQuiz} from '../../components/features/quiz/HeaderPageTests'
|
||||
import {Progressbar} from '../../components/features/quiz/ProgressbarQuiz'
|
||||
import {TaskQuiz} from '../../components/features/quiz/Task'
|
||||
import {useSelector} from "react-redux";
|
||||
import {selectedTest} from "../../redux/quizSlice";
|
||||
import React from "react";
|
||||
|
||||
export const QuizTestPage = () => {
|
||||
|
||||
let navigate = useNavigate()
|
||||
const test = useSelector(selectedTest)
|
||||
|
||||
if (!test) {
|
||||
return <Redirect to={'/quiz'}/>
|
||||
navigate('/quiz')
|
||||
}
|
||||
return (
|
||||
<>
|
||||
@ -19,4 +18,4 @@ export const QuizTestPage = () => {
|
||||
<TaskQuiz/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
import {Link, Redirect} from "react-router-dom"
|
||||
import {useNavigate} from "react-router-dom"
|
||||
import {HeaderPageTestsQuiz} from "../../components/features/quiz/HeaderPageTests"
|
||||
import {Results} from "../../components/features/quiz/Results";
|
||||
import {useSelector} from "react-redux";
|
||||
@ -10,8 +10,9 @@ export const ResultPage = () => {
|
||||
|
||||
const test = useSelector(selectedTest)
|
||||
|
||||
let navigate = useNavigate();
|
||||
if (!test) {
|
||||
return <Redirect to={'/quiz'}/>
|
||||
navigate('/quiz')
|
||||
}
|
||||
|
||||
return (
|
||||
@ -20,4 +21,4 @@ export const ResultPage = () => {
|
||||
<Results/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
};
|
@ -8,6 +8,8 @@ const initialState = {
|
||||
currentCandidate: {},
|
||||
auth: false,
|
||||
positionId: null,
|
||||
profileInfo: {},
|
||||
reportsDates: ''
|
||||
};
|
||||
|
||||
export const outstaffingSlice = createSlice({
|
||||
@ -37,11 +39,17 @@ export const outstaffingSlice = createSlice({
|
||||
},
|
||||
setUserInfo: (state, action) => {
|
||||
state.userInfo = action.payload;
|
||||
}
|
||||
},
|
||||
setProfileInfo: (state, action) => {
|
||||
state.profileInfo = action.payload;
|
||||
},
|
||||
setReportsDates: (state, action) => {
|
||||
state.reportsDates = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { tags, profiles, selectedItems, auth, currentCandidate, filteredCandidates, setPositionId, setUserInfo } = outstaffingSlice.actions;
|
||||
export const { tags, profiles, selectedItems, auth, currentCandidate, filteredCandidates, setPositionId, setUserInfo, setProfileInfo, setReportsDates } = outstaffingSlice.actions;
|
||||
|
||||
export const selectProfiles = (state) => state.outstaffing.profiles;
|
||||
export const selectTags = (state) => state.outstaffing.tags;
|
||||
@ -50,6 +58,8 @@ export const selectItems = (state) => state.outstaffing.selectedItems;
|
||||
export const selectCurrentCandidate = (state) => state.outstaffing.currentCandidate;
|
||||
export const selectAuth = (state) => state.outstaffing.auth;
|
||||
export const getPositionId = (state) => state.outstaffing.positionId;
|
||||
export const getProfileInfo = (state) => state.outstaffing.profileInfo;
|
||||
export const selectUserInfo = (state) => state.outstaffing.userInfo;
|
||||
export const getReportsDates = (state) => state.outstaffing.reportsDates;
|
||||
|
||||
export default outstaffingSlice.reducer;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {createAsyncThunk, createSlice} from '@reduxjs/toolkit';
|
||||
import {fetchGet, fetchPost} from './../server/server'
|
||||
import {fetchGet} from './../server/server'
|
||||
import axios from "axios";
|
||||
|
||||
|
||||
@ -17,17 +17,16 @@ export const setUserInfo = createAsyncThunk(
|
||||
'userInfo',
|
||||
async (id) => {
|
||||
try{
|
||||
const response = await fetchGet({
|
||||
return await fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/profile/get-main-data?user_id=${id}`,
|
||||
Origin: `${process.env.REACT_APP_BASE_URL}`,
|
||||
}
|
||||
)
|
||||
return response
|
||||
}catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export const fetchUserAnswersMany = createAsyncThunk(
|
||||
'answersUserMany',
|
||||
@ -38,13 +37,13 @@ export const fetchUserAnswersMany = createAsyncThunk(
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem('auth_token')}`,
|
||||
}
|
||||
})
|
||||
});
|
||||
return response.data
|
||||
}catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
export const fetchUserAnswerOne = createAsyncThunk(
|
||||
'answersUserOne',
|
||||
async (checkedValues) => {
|
||||
@ -54,55 +53,43 @@ export const fetchUserAnswerOne = createAsyncThunk(
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem('auth_token')}`,
|
||||
}
|
||||
})
|
||||
});
|
||||
return response.data
|
||||
}catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
export const fetchGetAnswers = createAsyncThunk(
|
||||
'answers',
|
||||
async (question_id) => {
|
||||
const resp = await fetchGet({
|
||||
return await fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/answer/get-answers?question_id=${question_id}`,
|
||||
Origin: `${process.env.REACT_APP_BASE_URL}`,
|
||||
}
|
||||
)
|
||||
return resp
|
||||
}
|
||||
)
|
||||
// export const fetchGetQuestion = createAsyncThunk(
|
||||
// 'questions',
|
||||
// async (uuid) => {
|
||||
// const resp = await fetchGet({
|
||||
// link: `${process.env.REACT_APP_API_URL}/api/question/get-questions?uuid=${uuid}`,
|
||||
// Origin: `${process.env.REACT_APP_BASE_URL}`,
|
||||
// }
|
||||
// )
|
||||
// return resp
|
||||
// }
|
||||
// )
|
||||
);
|
||||
|
||||
export const fetchResultTest = createAsyncThunk(
|
||||
'result',
|
||||
async (uuid) => {
|
||||
const resp = await fetchGet({
|
||||
return await fetchGet({
|
||||
link: `${process.env.REACT_APP_API_URL}/api/user-questionnaire/questionnaire-completed?user_questionnaire_uuid=${uuid}`,
|
||||
Origin: `${process.env.REACT_APP_BASE_URL}`,
|
||||
}
|
||||
)
|
||||
return resp
|
||||
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export const quizSlice = createSlice({
|
||||
name: 'quiz',
|
||||
initialState,
|
||||
reducers: {
|
||||
setQuestionnairesList: (state, action) => {
|
||||
state.dataQuestionnairesOfUser = action.payload
|
||||
state.passedTests = action.payload.filter(item=>item.status === 2)
|
||||
state.dataQuestionnairesOfUser = action.payload;
|
||||
state.passedTests = action.payload.filter(item => item.status === 2)
|
||||
},
|
||||
setSelectedTest: (state, action) => {
|
||||
state.selectedTest = action.payload
|
||||
@ -123,11 +110,10 @@ export const quizSlice = createSlice({
|
||||
|
||||
export const {setQuestionnairesList, setSelectedTest} = quizSlice.actions;
|
||||
|
||||
// export const selectQuestions = (state) => state.quiz.questions;
|
||||
|
||||
export const selectAnswer = (state) => state.quiz.answer;
|
||||
export const selectQuestionnairesOfUser = (state) => state.quiz.dataQuestionnairesOfUser;
|
||||
export const selectResult = (state) => state.quiz.result;
|
||||
export const selectIsLoading = (state) => state.quiz.isLoading;
|
||||
export const selectedTest = (state) => state.quiz.selectedTest;
|
||||
export const selectPassedTests = (state) => state.quiz.passedTests;
|
||||
export const selectUserInfo = (state) => state.quiz.userInfo;
|
||||
|
@ -2,6 +2,7 @@ import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
const initialState = {
|
||||
dateSelected: '',
|
||||
reportDate: ''
|
||||
};
|
||||
|
||||
export const reportSlice = createSlice({
|
||||
@ -11,11 +12,16 @@ export const reportSlice = createSlice({
|
||||
dateSelected: (state, action) => {
|
||||
state.dateSelected = action.payload;
|
||||
},
|
||||
setReportDate: (state, action) => {
|
||||
state.reportDate = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { dateSelected, } = reportSlice.actions;
|
||||
export const { dateSelected, setReportDate} = reportSlice.actions;
|
||||
|
||||
export const selectDate = (state) => state.report.dateSelected;
|
||||
|
||||
export const getReportDate = (state) => state.report.reportDate;
|
||||
|
||||
export default reportSlice.reducer;
|
||||
|
@ -1,22 +1,22 @@
|
||||
export const withAuthRedirect =
|
||||
(actionCall) =>
|
||||
({ link, params, history, role, logout, body }) => {
|
||||
const linkWithParams = params
|
||||
? `${link}?${new URLSearchParams(params)}`
|
||||
: link
|
||||
return actionCall(linkWithParams, body)
|
||||
.then((res) => {
|
||||
if (res.status && res.status === 401) {
|
||||
localStorage.clear()
|
||||
logout && logout()
|
||||
history.push(role === 'ROLE_DEV' ? '/authdev' : '/auth')
|
||||
}
|
||||
|
||||
return res
|
||||
})
|
||||
.catch((err) => {
|
||||
localStorage.clear()
|
||||
logout && logout()
|
||||
history.push(role === 'ROLE_DEV' ? '/authdev' : '/auth')
|
||||
})
|
||||
}
|
||||
// const linkWithParams = params
|
||||
// ? `${link}?${new URLSearchParams(params)}`
|
||||
// : link;
|
||||
// return actionCall(linkWithParams, body)
|
||||
// .then((res) => {
|
||||
// if (res.status && res.status === 401) {
|
||||
// localStorage.clear();
|
||||
// logout && logout();
|
||||
// history.push(role === 'ROLE_DEV' ? '/authdev' : '/auth')
|
||||
// }
|
||||
//
|
||||
// return res
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// localStorage.clear();
|
||||
// logout && logout();
|
||||
// history.push(role === 'ROLE_DEV' ? '/authdev' : '/auth')
|
||||
// })
|
||||
};
|
||||
|
@ -1,84 +1,5 @@
|
||||
import { withAuthRedirect } from './authRedirect'
|
||||
|
||||
export const fetchForm = withAuthRedirect(async (link, info) => {
|
||||
try {
|
||||
const response = await fetch(link, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
// 'Access-Control-Request-Headers': 'authorization',
|
||||
Authorization: `Bearer ${localStorage.getItem('auth_token')}`,
|
||||
Origin: `${process.env.REACT_APP_BASE_URL}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(info)
|
||||
})
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
console.log('Query error', error)
|
||||
}
|
||||
})
|
||||
|
||||
export const fetchAuth = async ({
|
||||
username,
|
||||
password,
|
||||
dispatch,
|
||||
catchError
|
||||
}) => {
|
||||
const apiURL = process.env.REACT_APP_API_URL
|
||||
try {
|
||||
const response = await fetch(`${apiURL}/api/user/login`, {
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
headers: {
|
||||
'Access-Control-Request-Headers': 'authorization',
|
||||
'Content-Type': 'application/json'
|
||||
// Origin: `http://localhost`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
password
|
||||
})
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
catchError()
|
||||
return response.statusText
|
||||
}
|
||||
|
||||
response.json().then((resJSON) => {
|
||||
localStorage.setItem('auth_token', resJSON.access_token)
|
||||
localStorage.setItem('id', resJSON.id)
|
||||
localStorage.setItem(
|
||||
'access_token_expired_at',
|
||||
resJSON.access_token_expired_at
|
||||
)
|
||||
dispatch(resJSON)
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error occured: ', error)
|
||||
}
|
||||
}
|
||||
|
||||
export const fetchReportList = withAuthRedirect(async (link) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://guild.loc/api/reports/index?user_id=26&fromDate=2021-10-18`,
|
||||
// link,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem('auth_token')}`
|
||||
}
|
||||
}
|
||||
)
|
||||
let data = await response.json()
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
console.log('Query error', error)
|
||||
}
|
||||
})
|
||||
|
||||
export const fetchGet = withAuthRedirect(async (link) => {
|
||||
try {
|
||||
@ -87,31 +8,11 @@ export const fetchGet = withAuthRedirect(async (link) => {
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem('auth_token')}`
|
||||
}
|
||||
})
|
||||
let data = await response.json()
|
||||
});
|
||||
let data = await response.json();
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
console.log('Query error', error)
|
||||
}
|
||||
})
|
||||
|
||||
export const fetchPost = withAuthRedirect(async (link, body) => {
|
||||
console.log('i', body)
|
||||
try {
|
||||
const response = await fetch(link, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem('auth_token')}`,
|
||||
'Content-Type': 'application/json',
|
||||
//Origin: `http://localhost:3000`
|
||||
Origin: `${process.env.REACT_APP_BASE_URL}`
|
||||
},
|
||||
body: JSON.stringify(body)
|
||||
})
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
console.log('Query error', error)
|
||||
}
|
||||
})
|
||||
});
|
||||
|