Переписываю спорные решения

This commit is contained in:
Дмитрий Савенко 2023-01-13 13:02:48 +03:00
parent 1234427b12
commit 75de4983c9
44 changed files with 875 additions and 39786 deletions

View File

@ -1,2 +1,2 @@
REACT_APP_API_URL = https://dev.itguild.info REACT_APP_API_URL = https://dev.itguild.info/api
REACT_APP_BASE_URL = https://dev.itguild.info REACT_APP_BASE_URL = https://dev.itguild.info/api

39090
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@
"@testing-library/user-event": "^12.8.3", "@testing-library/user-event": "^12.8.3",
"@typescript-eslint/eslint-plugin": "^4.5.0", "@typescript-eslint/eslint-plugin": "^4.5.0",
"@typescript-eslint/parser": "^4.5.0", "@typescript-eslint/parser": "^4.5.0",
"axios": "^0.21.1", "axios": "^0.24.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"babel-jest": "^26.6.0", "babel-jest": "^26.6.0",
"babel-loader": "8.1.0", "babel-loader": "8.1.0",
@ -65,7 +65,7 @@
"react-phone-input-2": "^2.14.0", "react-phone-input-2": "^2.14.0",
"react-redux": "^7.2.4", "react-redux": "^7.2.4",
"react-refresh": "^0.8.3", "react-refresh": "^0.8.3",
"react-router-dom": "^5.2.0", "react-router-dom": "^6.2.1",
"react-select": "^4.3.1", "react-select": "^4.3.1",
"react-syntax-highlighter": "^15.4.5", "react-syntax-highlighter": "^15.4.5",
"react-yandex-metrika": "^2.6.0", "react-yandex-metrika": "^2.6.0",
@ -84,7 +84,8 @@
"webpack": "4.44.2", "webpack": "4.44.2",
"webpack-dev-server": "3.11.1", "webpack-dev-server": "3.11.1",
"webpack-manifest-plugin": "2.2.0", "webpack-manifest-plugin": "2.2.0",
"workbox-webpack-plugin": "5.1.4" "workbox-webpack-plugin": "5.1.4",
"react-router": "latest"
}, },
"scripts": { "scripts": {
"start": "node scripts/start.js", "start": "node scripts/start.js",

View File

@ -1,12 +1,9 @@
import React from 'react' import React from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' import {BrowserRouter as Router, Route, Routes, Navigate} from 'react-router-dom';
import 'bootstrap/dist/css/bootstrap.min.css'
import './fonts/stylesheet.css'
import { ProtectedRoute } from './components/ProtectedRoute/ProtectedRoute'
import AuthPageForDevelopers from './pages/AuthPageForDevelopers' import AuthForPartners from "./pages/AuthForPartners/AuthForPartners";
import AuthPageForPartners from './pages/AuthPageForPartners' import AuthForDevelopers from "./pages/AuthForDevelopers/AuthForDevelopers";
import HomePage from './pages/HomePage' import HomePage from './pages/HomePage'
import CandidatePage from './pages/CandidatePage' import CandidatePage from './pages/CandidatePage'
import CalendarPage from './pages/CalendarPage' import CalendarPage from './pages/CalendarPage'
@ -14,59 +11,56 @@ import ReportPage from './pages/ReportFormPage.js'
import ProfileCalendarPage from './pages/ProfileCalendarPage.js' import ProfileCalendarPage from './pages/ProfileCalendarPage.js'
import FormPage from './pages/FormPage.js' import FormPage from './pages/FormPage.js'
import SingleReportPage from './pages/SingleReportPage' import SingleReportPage from './pages/SingleReportPage'
import { QuizPage } from './pages/quiz/QuizPage' import {QuizPage} from './pages/quiz/QuizPage'
import { InterjacentPage } from './pages/quiz/InterjacentPage' import {InterjacentPage} from './pages/quiz/InterjacentPage'
import { QuizTestPage } from './pages/quiz/QuizTestPage' import {QuizTestPage} from './pages/quiz/QuizTestPage'
import { InstructionPage } from './pages/quiz/InstructionPage' import {InstructionPage} from './pages/quiz/InstructionPage'
import { ResultPage } from './pages/quiz/ResultPage' import {ResultPage} from './pages/quiz/ResultPage'
import { Profile } from './pages/Profile.js' import {Profile} from './pages/Profile/Profile.js'
import { Summary } from './pages/Summary' import {Summary} from './pages/Summary/Summary'
import './fonts/stylesheet.css'
import 'bootstrap/dist/css/bootstrap.min.css'
const App = () => { const App = () => {
return ( return (
<> <>
<h1>IT Аутстаффинг в России</h1> <h1>IT Аутстаффинг в России</h1>
<Router> <Router>
<Switch>
<Route path='/authdev' exact> <Routes>
<AuthPageForDevelopers /> <Route exact path='/' element={<HomePage/>}/>
</Route>
<Route path='/auth' exact> <Route exact path='/authdev' element={<AuthForDevelopers/>}/>
<AuthPageForPartners /> <Route exact path='/auth' element={<AuthForPartners/>}/>
</Route>
<ProtectedRoute exact path='/' component={HomePage} /> <Route exact path='/candidate/:id' element={<CandidatePage/>}/>
<ProtectedRoute <Route exact path='/candidate/:id/form' element={<FormPage/>}/>
exact <Route path='/:userId/calendar' element={<CalendarPage/>}/>
path='/candidate/:id'
component={CandidatePage} <Route exact path='/report' element={<ReportPage/>}/>
/> <Route path='/report/:id' element={<SingleReportPage/>}/>
<ProtectedRoute path='/:userId/calendar' component={CalendarPage} />
<ProtectedRoute <Route path='/quiz' element={<QuizPage/>}/>
exact <Route path='/quiz-interjacent' element={<InterjacentPage/>}/>
path='/candidate/:id/form' <Route path='/quiz-test' element={<QuizTestPage/>}/>
component={FormPage} <Route path='/quiz-instruction' element={<InstructionPage/>}/>
/> <Route path='/quiz-result' element={<ResultPage/>}/>
<ProtectedRoute exact path='/report' component={ReportPage} />
<ProtectedRoute path='/report/:id' component={SingleReportPage} /> <Route exact path='/profile' element={<Profile/>}>
<ProtectedRoute path='/ProfileCalendar' component={ProfileCalendarPage} /> <Route exact path='calendar' element={<ProfileCalendarPage/>}/>
<ProtectedRoute path='/quiz' component={QuizPage} /> <Route exact path='summary' element={<Summary/>}/>
<ProtectedRoute </Route>
path='/quiz-interjacent'
component={InterjacentPage} {/*<Route component={() => <div>Page not found</div>} />*/}
/> {/*<Route path='*' component={() => <div>Page not found</div>}/>*/}
<ProtectedRoute path='/quiz-test' component={QuizTestPage} />
<ProtectedRoute <Route path="*" element={<Navigate to="/" replace/>}/>
path='/quiz-instruction' </Routes>
component={InstructionPage} </Router>
/> </>
<ProtectedRoute path='/profile' component={Profile} />
<ProtectedRoute path='/summary' component={Summary} />
<ProtectedRoute path='/quiz-result' component={ResultPage} />
<ProtectedRoute component={() => <div>Page not found</div>} />
</Switch>
</Router>
</>
) )
} };
export default App export default App

48
src/api/request.js Normal file
View 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;

View File

@ -1,107 +1,129 @@
import React, { useState } from 'react' import React, {useState} from 'react'
import { Link } from 'react-router-dom' import {Link} from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux' 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 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 './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 SweetAlert = withSwalInstance(swal);
const dispatch = useDispatch()
const isLoading = useSelector(selectIsLoading) export const AuthBox = ({title, altTitle, roleChangeLink}) => {
const dispatch = useDispatch();
const [username, setUsername] = useState('') const {apiRequest} = useRequest();
const [password, setPassword] = useState('')
const [error, setError] = useState(null) 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 ( return (
<div className='auth-box'> <div className='auth-box'>
<h2 className='auth-box__header'> <h2 className='auth-box__header'>
Войти в <span>систему</span> Войти в <span>систему</span>
</h2> </h2>
<div className='auth-box__title'> <div className='auth-box__title'>
<img src={ellipse} alt='' /> <img src={ellipse} alt=''/>
<span>{title}</span> <span>{title}</span>
</div>
<form className='auth-box__form'>
<label htmlFor='login'>Ваш логин:</label>
<input
id='login'
type='text'
placeholder='Логин'
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<label htmlFor='password'>Пароль:</label>
<input
id='password'
type='password'
placeholder='Пароль'
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
{error && (
<div className='auth-box__form-error'>
<SweetAlert
show={!!error}
title='Ошибка'
text={error}
onConfirm={() => setError(null)}
/>
</div>
)}
<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))
}
})
}
: () => {}
}
>
{isLoading ? <Loader /> : 'Войти'}
</button>
<Link to={roleChangeLink}>
<button className='auth-box__form-btn--role auth-box__auth-link'>
{altTitle}
</button>
</Link>
</div> </div>
</form> <form className='auth-box__form'>
</div> <label htmlFor='login'>Ваш логин:</label>
<input
id='login'
type='text'
placeholder='Логин'
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<label htmlFor='password'>Пароль:</label>
<input
id='password'
type='password'
placeholder='Пароль'
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
{error && (
<div className='auth-box__form-error'>
<SweetAlert
show={!!error}
title='Ошибка'
text={error}
onConfirm={() => setError(null)}
/>
</div>
)}
<div className='auth-box__form-buttons'>
<button
className='auth-box__form-btn'
onClick={(e) => {
e.preventDefault();
submitHandler()
}}
>
{isLoading ? <Loader/> : 'Войти'}
</button>
<Link to={roleChangeLink}>
<button className='auth-box__form-btn--role auth-box__auth-link'>
{altTitle}
</button>
</Link>
</div>
</form>
</div>
) )
} };

View File

@ -1,5 +1,5 @@
import React, {useEffect} from 'react' import React, {useEffect} from 'react'
import {useHistory, useParams, Link} from 'react-router-dom' import {useParams, Link, useNavigate} from 'react-router-dom'
import {useSelector, useDispatch} from 'react-redux' import {useSelector, useDispatch} from 'react-redux'
import { import {
currentCandidate, currentCandidate,
@ -22,8 +22,8 @@ import {Footer} from '../Footer/Footer'
import './candidate.scss' import './candidate.scss'
const Candidate = () => { const Candidate = () => {
const history = useHistory();
const {id: candidateId} = useParams(); const {id: candidateId} = useParams();
const navigate = useNavigate();
const dispatch = useDispatch(); const dispatch = useDispatch();
const role = useSelector(getRole); const role = useSelector(getRole);
const [activeSnippet, setActiveSnippet] = useState(true); const [activeSnippet, setActiveSnippet] = useState(true);
@ -36,7 +36,6 @@ const Candidate = () => {
fetchGet({ fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/profile/${candidateId}`, link: `${process.env.REACT_APP_API_URL}/api/profile/${candidateId}`,
params: Number(candidateId), params: Number(candidateId),
history,
role, role,
logout: () => dispatch(auth(false)) logout: () => dispatch(auth(false))
}).then((el) => dispatch(currentCandidate(el))) }).then((el) => dispatch(currentCandidate(el)))
@ -97,7 +96,7 @@ const Candidate = () => {
<div className='row'> <div className='row'>
<div className='col-12 candidate__header'> <div className='col-12 candidate__header'>
<div className='candidate__arrow' onClick={() => history.push('/')}> <div className='candidate__arrow' onClick={() => navigate('/')}>
<div className='candidate__arrow-img'> <div className='candidate__arrow-img'>
<img src={arrow} alt=''/> <img src={arrow} alt=''/>
</div> </div>

View File

@ -1,43 +1,42 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import male from '../../images/medium_male.png' import male from '../../images/medium_male.png'
import rectangle from '../../images/rectangle_secondPage.png' import rectangle from '../../images/rectangle_secondPage.png'
import { Link, useHistory } from 'react-router-dom' import { Link } from 'react-router-dom'
import { LEVELS, SKILLS } from '../constants/constants' import { LEVELS, SKILLS } from '../constants/constants'
import { import {
selectProfiles, selectProfiles,
selectFilteredCandidates, selectFilteredCandidates,
selectItems, selectItems,
auth
} from '../../redux/outstaffingSlice' } from '../../redux/outstaffingSlice'
import { useSelector, useDispatch } from 'react-redux' import { useSelector, useDispatch } from 'react-redux'
import { fetchGet } from '../../server/server'
import { Loader } from '../Loader/Loader' import { Loader } from '../Loader/Loader'
import { getRole } from '../../redux/roleSlice' import { getRole } from '../../redux/roleSlice'
import './description.scss' import './description.scss'
import {useRequest} from "../../hooks/useRequest";
const Description = ({ onLoadMore, isLoadingMore }) => { const Description = ({ onLoadMore, isLoadingMore }) => {
const dispatch = useDispatch() const dispatch = useDispatch();
const [isLoaded, setIsLoaded] = useState(false) const {apiRequest} = useRequest();
const history = useHistory() const [isLoaded, setIsLoaded] = useState(false);
const role = useSelector(getRole) const role = useSelector(getRole);
const candidatesListArr = useSelector(selectProfiles) const candidatesListArr = useSelector(selectProfiles);
const itemsArr = useSelector(selectItems) const itemsArr = useSelector(selectItems);
const filteredListArr = useSelector(selectFilteredCandidates) const filteredListArr = useSelector(selectFilteredCandidates);
const [allCandidates, getAllCandidates] = useState([]) const [allCandidates, getAllCandidates] = useState([]);
useEffect(() => { // useEffect(() => {
fetchGet({ // apiRequest('/profile?limit=',{
link: `${process.env.REACT_APP_API_URL}/api/profile?limit=`, // params: 1000,
params: 1000, //
history, // role,
role, // logout: () => dispatch(auth(false))
logout: () => dispatch(auth(false)) // }).then((p) => {
}).then((p) => { // getAllCandidates(p);
getAllCandidates(p) // setIsLoaded(true)
setIsLoaded(true) // })
}) // }, []);
}, [])
if (!filteredListArr) { if (!filteredListArr) {
return ( return (
@ -242,6 +241,6 @@ const Description = ({ onLoadMore, isLoadingMore }) => {
</div> </div>
</section> </section>
) )
} };
export default Description export default Description

View File

@ -1,7 +1,7 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import { fetchPost } from '../../server/server' import { fetchPost } from '../../server/server'
import { auth } from '../../redux/outstaffingSlice' 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 { Loader } from '../Loader/Loader'
import PhoneInput from 'react-phone-input-2' import PhoneInput from 'react-phone-input-2'
import 'react-phone-input-2/lib/style.css' import 'react-phone-input-2/lib/style.css'
@ -16,7 +16,7 @@ const SweetAlert = withSwalInstance(swal)
const Form = () => { const Form = () => {
const dispatch = useDispatch() const dispatch = useDispatch()
const history = useHistory() const navigate = useNavigate()
const role = useSelector(getRole) const role = useSelector(getRole)
const urlParams = useParams() const urlParams = useParams()
const [status, setStatus] = useState(null) const [status, setStatus] = useState(null)
@ -52,7 +52,7 @@ const Form = () => {
profile_id: urlParams.id, profile_id: urlParams.id,
...data ...data
}, },
history,
role, role,
logout: () => dispatch(auth(false)) logout: () => dispatch(auth(false))
}).then((res) => }).then((res) =>
@ -79,8 +79,8 @@ const Form = () => {
setStatus(null) setStatus(null)
} }
: () => { : () => {
setStatus(null) setStatus(null);
history.push(`/candidate/${urlParams.id}`) navigate(`/candidate/${urlParams.id}`)
} }
} }
/> />

View File

@ -1,66 +1,72 @@
import React, { useState, useEffect } from 'react' import React, {useState, useEffect} from 'react'
import { useDispatch, useSelector } from 'react-redux' import {useDispatch, useSelector} from 'react-redux'
import Outstaffing from '../Outstaffing/Outstaffing' import Outstaffing from '../Outstaffing/Outstaffing'
import Description from '../Description/Description' import Description from '../Description/Description'
import { fetchGet } from '../../server/server' import {Footer} from '../Footer/Footer'
import { profiles, tags, auth } from '../../redux/outstaffingSlice'
import { getRole } from '../../redux/roleSlice'
import { Footer } from '../Footer/Footer' import {profiles, tags, auth} from '../../redux/outstaffingSlice'
import { useHistory } from 'react-router-dom' import {getRole} from '../../redux/roleSlice'
import {useRequest} from "../../hooks/useRequest";
const Home = () => { const Home = () => {
const history = useHistory()
const [isLoadingMore, setIsLoadingMore] = useState(false)
const [index, setIndex] = useState(4)
const dispatch = useDispatch() const [isLoadingMore, setIsLoadingMore] = useState(false);
const role = useSelector(getRole) const [index, setIndex] = useState(4);
const dispatch = useDispatch();
const role = useSelector(getRole);
const {apiRequest} = useRequest();
useEffect(() => { useEffect(() => {
setIsLoadingMore(true) setIsLoadingMore(true);
fetchGet({ apiRequest('/profile',{
link: `${process.env.REACT_APP_API_URL}/api/profile?limit=`, //Корс блокирует все фильтры в гет параметрах
params: index, // params: {"get-document-list": 1000},
history,
role, role,
logout: () => dispatch(auth(false)) // logout: () => dispatch(auth(false))
}).then((profileArr) => { }).then((profileArr) => {
dispatch(profiles(profileArr))
setIsLoadingMore(false)
})
fetchGet({ dispatch(profiles(profileArr));
link: `${process.env.REACT_APP_API_URL}/api/skills/skills-on-main-page`, setIsLoadingMore(false)
history, });
apiRequest('/skills/skills-on-main-page',{
role, role,
logout: () => dispatch(auth(false)) // logout: () => dispatch(auth(false))
}).then((skills) => { }).then((skills) => {
if (!skills) { if (!skills) {
return [] 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) => const tempTags = values.map((value, index) =>
value.map((val) => { 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(tags(tempTags))
}) })
}, [dispatch, index]) }, [dispatch, index]);
const loadMore = (count) => { const loadMore = (count) => {
setIndex((prev) => prev + count) setIndex((prev) => prev + count)
} };
return ( return (
<> <>
<Outstaffing /> <Outstaffing/>
<Description onLoadMore={loadMore} isLoadingMore={isLoadingMore} /> <Description onLoadMore={loadMore} isLoadingMore={isLoadingMore}/>
<Footer /> <Footer/>
</> </>
) )
} };
export default Home export default Home

View File

@ -1,5 +1,6 @@
import SVGLoader from 'react-loader-spinner' import SVGLoader from 'react-loader-spinner'
import './loader.scss' import './loader.scss'
import React from "react";
export const Loader = ({ width = 50, height = 50 }) => { export const Loader = ({ width = 50, height = 50 }) => {
return ( return (
@ -7,4 +8,4 @@ export const Loader = ({ width = 50, height = 50 }) => {
<SVGLoader type='Circles' color='#fff' height={height} width={width} /> <SVGLoader type='Circles' color='#fff' height={height} width={width} />
</div> </div>
) )
} };

View File

@ -1,5 +1,5 @@
import React, {useState} from 'react' import React, {useState} from 'react'
import {useHistory} from 'react-router-dom' import {useNavigate} from 'react-router-dom'
import {useDispatch, useSelector} from 'react-redux' import {useDispatch, useSelector} from 'react-redux'
import {Loader} from '../Loader/Loader' import {Loader} from '../Loader/Loader'
import {auth} from '../../redux/outstaffingSlice' import {auth} from '../../redux/outstaffingSlice'
@ -11,7 +11,7 @@ export const LogoutButton = () => {
const [isLoggingOut, setIsLoggingOut] = useState(false); const [isLoggingOut, setIsLoggingOut] = useState(false);
const dispatch = useDispatch(); const dispatch = useDispatch();
const userRole = useSelector(getRole); const userRole = useSelector(getRole);
const history = useHistory(); const navigate = useNavigate();
return ( return (
<div className='logout-button'> <div className='logout-button'>
@ -21,7 +21,7 @@ export const LogoutButton = () => {
localStorage.clear(); localStorage.clear();
dispatch(auth(false)); dispatch(auth(false));
setIsLoggingOut(false); setIsLoggingOut(false);
history.push(userRole === 'ROLE_DEV' ? '/authdev' : '/auth') navigate(userRole === 'ROLE_DEV' ? '/authdev' : '/auth')
}} }}
> >
{isLoggingOut ? <Loader/> : 'Выйти'}{' '} {isLoggingOut ? <Loader/> : 'Выйти'}{' '}

View File

@ -21,18 +21,18 @@ const createSelectPositionHandler =
} else { } else {
dispatch(setPositionId(id)) dispatch(setPositionId(id))
} }
} };
const Outstaffing = () => { const Outstaffing = () => {
const dispatch = useDispatch() const dispatch = useDispatch();
const positionId = useSelector(getPositionId) const positionId = useSelector(getPositionId);
const tagsArr = useSelector(selectTags) const tagsArr = useSelector(selectTags);
const onSelectPosition = createSelectPositionHandler({ const onSelectPosition = createSelectPositionHandler({
positionId, positionId,
setPositionId, setPositionId,
dispatch dispatch
}) });
return ( return (
<> <>
<section className='outstaffing'> <section className='outstaffing'>
@ -91,6 +91,6 @@ const Outstaffing = () => {
<TagSelect /> <TagSelect />
</> </>
) )
} };
export default Outstaffing export default Outstaffing

View File

@ -9,7 +9,7 @@ import {
} from '../../redux/outstaffingSlice' } from '../../redux/outstaffingSlice'
import { fetchGet } from '../../server/server' import { fetchGet } from '../../server/server'
import { useHistory } from 'react-router-dom'
import { getRole } from '../../redux/roleSlice' import { getRole } from '../../redux/roleSlice'
import './outstaffingBlock.scss' import './outstaffingBlock.scss'
@ -19,35 +19,32 @@ const handlePositionClick = ({
positionId, positionId,
isSelected, isSelected,
onSelect, onSelect,
history,
role role
}) => { }) => {
if (isSelected) { if (isSelected) {
fetchGet({ fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/profile?limit=`, link: `${process.env.REACT_APP_API_URL}/api/profile?limit=`,
params: 4, params: 4,
history,
role, role,
logout: () => dispatch(auth(false)) logout: () => dispatch(auth(false))
}).then((profileArr) => { }).then((profileArr) => {
dispatch(filteredCandidates(profileArr)) dispatch(filteredCandidates(profileArr));
dispatch(selectedItems([])) dispatch(selectedItems([]));
onSelect(positionId) onSelect(positionId)
}) })
} else { } else {
fetchGet({ fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/profile?position_id=`, link: `${process.env.REACT_APP_API_URL}/api/profile?position_id=`,
params: positionId, params: positionId,
history,
role, role,
logout: () => dispatch(auth(false)) logout: () => dispatch(auth(false))
}).then((el) => { }).then((el) => {
dispatch(filteredCandidates(el)) dispatch(filteredCandidates(el));
dispatch(selectedItems([])) dispatch(selectedItems([]));
onSelect(positionId) onSelect(positionId)
}) })
} }
} };
const OutstaffingBlock = ({ const OutstaffingBlock = ({
dataTags = [], dataTags = [],
@ -58,20 +55,20 @@ const OutstaffingBlock = ({
isSelected, isSelected,
onSelect onSelect
}) => { }) => {
const history = useHistory()
const role = useSelector(getRole)
const dispatch = useDispatch() const role = useSelector(getRole);
const itemsArr = useSelector(selectItems) const dispatch = useDispatch();
const itemsArr = useSelector(selectItems);
const handleBlockClick = (item, id) => { const handleBlockClick = (item, id) => {
if (!itemsArr.find((el) => item === el.value)) { 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) => { dataTags.forEach((el) => {
if (el.name === 'skills_back') { if (el.name === 'skills_back') {
@ -81,7 +78,7 @@ const OutstaffingBlock = ({
} else if (el.name === 'skills_front') { } else if (el.name === 'skills_front') {
classes = 'front' classes = 'front'
} }
}) });
return ( return (
<OutsideClickHandler <OutsideClickHandler
@ -104,7 +101,6 @@ const OutstaffingBlock = ({
positionId, positionId,
isSelected, isSelected,
onSelect, onSelect,
history,
role role
}) })
} }
@ -136,6 +132,6 @@ const OutstaffingBlock = ({
</div> </div>
</OutsideClickHandler> </OutsideClickHandler>
) )
} };
export default OutstaffingBlock export default OutstaffingBlock

View File

@ -1,59 +0,0 @@
import React, {useEffect, useState} from 'react';
import {auth, getProfileInfo, setProfileInfo} from "../../redux/outstaffingSlice";
import {useDispatch, useSelector} from "react-redux";
import {Loader} from '../Loader/Loader'
import {getRole} from "../../redux/roleSlice";
import {useHistory, NavLink} from "react-router-dom";
import {fetchGet} from "../../server/server";
import './profileHeader.scss'
export const ProfileHeader = () => {
const [isLoggingOut, setIsLoggingOut] = useState(false);
const dispatch = useDispatch();
const userRole = useSelector(getRole);
const profileInfo = useSelector(getProfileInfo)
const history = useHistory();
useEffect(() => {
fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/profile/${localStorage.getItem('cardId')}`,
}).then((profileInfo) => {
dispatch(setProfileInfo(profileInfo))
})
}, [])
return(
<header className='profileHeader'>
<div className='profileHeader__head'>
<div className='profileHeader__container'>
<h2 className='profileHeader__title'>itguild.<span>для разработчиков</span></h2>
<button onClick={() => {
setIsLoggingOut(true);
localStorage.clear();
dispatch(auth(false));
setIsLoggingOut(false);
history.push(userRole === 'ROLE_DEV' ? '/authdev' : '/auth')
}}
className='profileHeader__logout'>
{isLoggingOut ? <Loader/> : 'Выйти'}{' '}
</button>
</div>
</div>
<div className='profileHeader__info'>
<div className='profileHeader__container'>
<nav className='profileHeader__nav'>
<NavLink to={'/summary'}>Резюме</NavLink>
<NavLink to={'/profile'}>Отчетность</NavLink>
<a>Трекер</a>
<a>Выплаты</a>
<a>Настройки</a>
</nav>
<div className='profileHeader__personalInfo'>
<h3 className='profileHeader__personalInfoName'>{profileInfo.fio}</h3>
<img src={profileInfo.photo} className='profileHeader__personalInfoAvatar' />
</div>
</div>
</div>
</header>
)
}

View 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(`/api/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 to={'/summary'}>Резюме</NavLink>
<NavLink to={'/profile'}>Отчетность</NavLink>
<NavLink to={'/profile'}>Трекер</NavLink>
<NavLink to={'/profile'}>Выплаты</NavLink>
<NavLink to={'/profile'}>Настройки</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>
)
};

View File

@ -8,16 +8,16 @@ export const ProtectedRoute = ({ component: Component, ...rest }) => {
const existingToken = localStorage.getItem('auth_token') const existingToken = localStorage.getItem('auth_token')
const expiresAt = localStorage.getItem('access_token_expired_at') 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 ( return (
<Route <Route
{...rest} {...rest}
render={props => render={props =>
( isAuth || isTokenAlive) ? ( // ( isAuth || isTokenAlive) ? (
<Component {...props} /> <Component {...props} />
) : <Redirect to='/auth' /> // ) : <Redirect to='/auth' />
} }
/> />
); );
} };

View File

@ -1,7 +1,7 @@
import React, {useState} from 'react' import React, {useState} from 'react'
import {useSelector, useDispatch} from 'react-redux' import {useSelector, useDispatch} from 'react-redux'
import {fetchPost} from '../../server/server' import {fetchPost} from '../../server/server'
import {useHistory, useParams, Redirect, Link} from 'react-router-dom' import {useNavigate, useParams, Redirect, Link} from 'react-router-dom'
import {Loader} from '../Loader/Loader' import {Loader} from '../Loader/Loader'
import {auth} from '../../redux/outstaffingSlice' import {auth} from '../../redux/outstaffingSlice'
import {getReportDate} from '../../redux/reportSlice' import {getReportDate} from '../../redux/reportSlice'
@ -28,7 +28,7 @@ const getCreatedDate = (day) => {
const ReportForm = () => { const ReportForm = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const history = useHistory(); const navigate = useNavigate();
const reportDate = useSelector(getReportDate); const reportDate = useSelector(getReportDate);
const role = useSelector(getRole); const role = useSelector(getRole);
@ -153,7 +153,6 @@ const ReportForm = () => {
<button className='report-form__footer-btn' onClick={() => { <button className='report-form__footer-btn' onClick={() => {
fetchPost({ fetchPost({
link: `${process.env.REACT_APP_API_URL}/api/reports/create`, link: `${process.env.REACT_APP_API_URL}/api/reports/create`,
history,
role, role,
body: { body: {
tasks: inputs, tasks: inputs,

View File

@ -12,38 +12,37 @@ import {
auth auth
} from '../../redux/outstaffingSlice' } from '../../redux/outstaffingSlice'
import { fetchGet } from '../../server/server' import { fetchGet } from '../../server/server'
import { useHistory } from 'react-router-dom'
import { getRole } from '../../redux/roleSlice' import { getRole } from '../../redux/roleSlice'
const TagSelect = () => { const TagSelect = () => {
const history = useHistory
const role = useSelector(getRole)
const [searchLoading, setSearchLoading] = useState(false)
const dispatch = useDispatch()
const itemsArr = useSelector(selectItems) const role = useSelector(getRole);
const [searchLoading, setSearchLoading] = useState(false);
const dispatch = useDispatch();
const tagsArr = useSelector(selectTags) const itemsArr = useSelector(selectItems);
const tagsArr = useSelector(selectTags);
const handleSubmit = ({ dispatch, setSearchLoading }) => { const handleSubmit = ({ dispatch, setSearchLoading }) => {
setSearchLoading(true) setSearchLoading(true);
dispatch(setPositionId(null)) dispatch(setPositionId(null));
const filterItemsId = itemsArr.map((item) => item.id).join() const filterItemsId = itemsArr.map((item) => item.id).join();
fetchGet({ fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/profile?skills=`, link: `${process.env.REACT_APP_API_URL}/api/profile?skills=`,
params: filterItemsId, params: filterItemsId,
history,
role, role,
logout: () => dispatch(auth(false)) logout: () => dispatch(auth(false))
}).then((el) => { }).then((el) => {
dispatch(filteredCandidates(el)) dispatch(filteredCandidates(el));
setSearchLoading(false) setSearchLoading(false)
}) })
// dispatch(selectedItems([])); // dispatch(selectedItems([]));
} };
return ( return (
<> <>
@ -85,6 +84,6 @@ const TagSelect = () => {
</section> </section>
</> </>
) )
} };
export default TagSelect export default TagSelect

View File

@ -5,20 +5,20 @@ import {fetchGet} from "../../../server/server";
export const Results = () => { export const Results = () => {
const result = useSelector(selectResult) const result = useSelector(selectResult);
const test = useSelector(selectedTest) const test = useSelector(selectedTest);
const [maxScore, setMaxScore] = useState('') const [maxScore, setMaxScore] = useState('');
const dispatch = useDispatch() const dispatch = useDispatch();
useEffect(async () => { useEffect(async () => {
dispatch(fetchResultTest(test.uuid)) dispatch(fetchResultTest(test.uuid));
const response = await fetchGet({ const response = await fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/user-questionnaire/get-points-number?user_questionnaire_uuid=${test.uuid}`, 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}`, Origin: `${process.env.REACT_APP_BASE_URL}`,
} }
) );
setMaxScore(response.sum_point) setMaxScore(response.sum_point)
}, []) }, []);
return ( return (
<div className={'result _container'}> <div className={'result _container'}>

View File

@ -1,59 +1,62 @@
import React, {useEffect} from 'react' import React, {useEffect, useState} from 'react'
import {useHistory} from "react-router" import { useNavigate} from "react-router-dom"
import {CodeSnippetlighter} from '../../../pages/CodeSnippetPage' import {useSelector, useDispatch} from 'react-redux'
import './quiz.scss'
import {useDispatch} from 'react-redux'
import {useState} from 'react'
import { import {
fetchGetAnswers, fetchGetAnswers,
selectAnswer, selectAnswer,
selectedTest selectedTest
} from '../../../redux/quizSlice' } from '../../../redux/quizSlice'
import {useSelector} from 'react-redux'
import {Progressbar} from './ProgressbarQuiz' import {Progressbar} from './ProgressbarQuiz'
import {fetchUserAnswersMany, fetchUserAnswerOne} from './../../../redux/quizSlice'
import {GetOptionTask} from './GetOptionTask' import {GetOptionTask} from './GetOptionTask'
import {fetchUserAnswersMany, fetchUserAnswerOne} from './../../../redux/quizSlice'
import {fetchGet} from "../../../server/server"; import {fetchGet} from "../../../server/server";
import './quiz.scss'
export const TaskQuiz = () => { export const TaskQuiz = () => {
const history = useHistory(); const navigate = useNavigate();
const dispatch = useDispatch() const dispatch = useDispatch();
const listAnswers = useSelector(selectAnswer)
const dataTest = useSelector(selectedTest)
const [index, setIndex] = useState(0);
const [checkedValues, setCheckedValues] = useState([])
const [stripValue, setStripValue] = useState(0);
const [inputValue, setInputValue] = useState('')
const id = localStorage.getItem('id');
const [questions, setQuestions] = useState([])
useEffect(async () => { const listAnswers = useSelector(selectAnswer);
const response = await fetchGet({ const dataTest = useSelector(selectedTest);
const [index, setIndex] = useState(0);
const [checkedValues, setCheckedValues] = useState([]);
const [stripValue, setStripValue] = useState(0);
const [inputValue, setInputValue] = useState('');
const [questions, setQuestions] = useState([]);
const id = localStorage.getItem('id');
useEffect( () => {
const response = fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/question/get-questions?uuid=${dataTest.uuid}`, link: `${process.env.REACT_APP_API_URL}/api/question/get-questions?uuid=${dataTest.uuid}`,
Origin: `${process.env.REACT_APP_BASE_URL}`, Origin: `${process.env.REACT_APP_BASE_URL}`,
} }
) );
setQuestions(response) setQuestions(response);
dispatch(fetchGetAnswers(response[0].id)) dispatch(fetchGetAnswers(response[0].id));
setStripValue((+index + 1) * 100 / response.length) setStripValue((+index + 1) * 100 / response.length)
}, [dispatch]) }, [dispatch]);
const nextQuestion = async (e) => { const nextQuestion = async (e) => {
e.preventDefault() e.preventDefault();
//Проверка на валидацию ответов //Проверка на валидацию ответов
if (checkedValues.length || inputValue) { if (checkedValues.length || inputValue) {
switch (questions[index].question_type_id) { switch (questions[index].question_type_id) {
case '3': case '3':
dispatch(fetchUserAnswersMany(checkedValues)) dispatch(fetchUserAnswersMany(checkedValues));
break; break;
case '2': case '2':
case '1': case '1':
case '4': case '4':
dispatch(fetchUserAnswerOne(checkedValues)) dispatch(fetchUserAnswerOne(checkedValues));
break; break;
default: default:
break; break;
@ -61,20 +64,20 @@ export const TaskQuiz = () => {
//Проверка на существование следующего запроса //Проверка на существование следующего запроса
if (index < questions.length - 1) { if (index < questions.length - 1) {
await dispatch(fetchGetAnswers(questions[index + 1].id)) await dispatch(fetchGetAnswers(questions[index + 1].id));
setIndex(prev => prev >= questions.length - 1 ? prev : prev + 1) setIndex(prev => prev >= questions.length - 1 ? prev : prev + 1);
setStripValue((prev => prev + (100 / questions.length))) setStripValue((prev => prev + (100 / questions.length)));
setCheckedValues([]); setCheckedValues([]);
setInputValue('') setInputValue('')
} else { } else {
history.push(`/quiz-result`) navigate(`/quiz-result`);
alert("Тест пройден!") alert("Тест пройден!")
} }
} else { } else {
alert("Вы не ответили на вопрос") alert("Вы не ответили на вопрос")
} }
} };
const handleChange = (e) => { const handleChange = (e) => {
const checked = e.target.checked; const checked = e.target.checked;
@ -86,8 +89,8 @@ export const TaskQuiz = () => {
question_id: questions[index].id, question_id: questions[index].id,
response_body: e.target.value response_body: e.target.value
}]) : }]) :
setCheckedValues(prev => [...prev.filter(item => item.response_body !== e.target.value)]) setCheckedValues(prev => [...prev.filter(item => item.response_body !== e.target.value)]);
break break;
case '1': case '1':
case '2': case '2':
case '4': case '4':
@ -146,4 +149,4 @@ export const TaskQuiz = () => {
</div> </div>
</React.StrictMode> </React.StrictMode>
) )
} };

View File

@ -18,3 +18,20 @@ export function transformHtml(text) {
); );
return {__html: finalHtml.join('')} 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}`;

20
src/hooks/useLogout.js Normal file
View File

@ -0,0 +1,20 @@
import React from "react";
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));
console.log('logout')
navigate(userRole === 'ROLE_DEV' ? '/authdev' : '/auth')
};
return {logout}
};

64
src/hooks/useRequest.js Normal file
View File

@ -0,0 +1,64 @@
import React from "react";
import axios from 'axios';
import {getToken, urlHasParams} from "../helper";
import {useNavigate} from "react-router";
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) {
console.log(response, 'LOGUTATAT')
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}
};

View File

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import { AuthBox } from '../AuthBox/AuthBox' import { AuthBox } from '../../components/AuthBox/AuthBox'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import arrow from '../../images/arrow__login_page.png' 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 vector from '../../images/Vector_Smart_Object.png'
import { selectAuth } from '../../redux/outstaffingSlice' import { selectAuth } from '../../redux/outstaffingSlice'
import { Redirect } from 'react-router-dom' import { useNavigate} from 'react-router-dom'
import { Footer } from '../Footer/Footer' import { Footer } from '../../components/Footer/Footer'
import './authForDevelopers.scss' import './authForDevelopers.scss'
const AuthForDevelopers = () => { const AuthForDevelopers = () => {
const isAuth = useSelector(selectAuth)
const isAuth = useSelector(selectAuth);
let navigate = useNavigate();
if (isAuth) { if (isAuth) {
return <Redirect to='/profile' /> navigate('/profile')
} }
return ( return (
@ -89,6 +91,6 @@ const AuthForDevelopers = () => {
</div> </div>
</section> </section>
) )
} };
export default AuthForDevelopers export default AuthForDevelopers

View File

@ -7,18 +7,19 @@ import vector from '../../images/Vector_Smart_Object.png'
import vectorBlack from '../../images/Vector_Smart_Object_black.png' import vectorBlack from '../../images/Vector_Smart_Object_black.png'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { selectAuth } from '../../redux/outstaffingSlice' import { selectAuth } from '../../redux/outstaffingSlice'
import { Redirect } from 'react-router-dom' import { useNavigate} from 'react-router-dom'
import { Footer } from '../Footer/Footer' import { Footer } from '../../components/Footer/Footer'
import { AuthBox } from '../AuthBox/AuthBox' import { AuthBox } from '../../components/AuthBox/AuthBox'
import './authForPartners.scss' import './authForPartners.scss'
const AuthForPartners = () => { const AuthForPartners = () => {
const isAuth = useSelector(selectAuth) const isAuth = useSelector(selectAuth);
let navigate = useNavigate();
if (isAuth) { if (isAuth) {
return <Redirect to='/' /> navigate('/')
} }
return ( return (
@ -81,6 +82,6 @@ const AuthForPartners = () => {
</div> </div>
</section> </section>
) )
} };
export default AuthForPartners export default AuthForPartners

View File

@ -1,8 +0,0 @@
import React from 'react';
import AuthForDevelopers from '../components/Auth/AuthForDevelopers';
const AuthPageForDevelopers = () => {
return <AuthForDevelopers />;
};
export default AuthPageForDevelopers;

View File

@ -1,8 +0,0 @@
import React from 'react';
import AuthForPartners from '../components/Auth/AuthForPartners';
const AuthPageForPartners = () => {
return <AuthForPartners />;
};
export default AuthPageForPartners;

View File

@ -1,11 +1,11 @@
import React from 'react'; import React from 'react';
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router-dom';
import { WithLogout } from '../hoc/withLogout'; import { WithLogout } from '../hoc/withLogout';
import Calendar from '../components/Calendar/Calendar'; import Calendar from '../components/Calendar/Calendar';
const CalendarPage = () => { const CalendarPage = () => {
const history = useHistory(); const navigate = useNavigate();
return <WithLogout><Calendar onSelect={() => { history.push('/report/0') }} /></WithLogout>; return <WithLogout><Calendar onSelect={() => { navigate('/report/0') }} /></WithLogout>;
}; };
export default CalendarPage; export default CalendarPage;

View File

@ -1,98 +1,99 @@
import React from 'react' import React from 'react'
import { useDispatch, useSelector } from 'react-redux' import {useDispatch, useSelector} from 'react-redux'
import { useHistory, useParams } from 'react-router-dom' import {useParams, useNavigate} from 'react-router-dom'
import { import {
currentCandidate, currentCandidate,
selectCurrentCandidate, selectCurrentCandidate,
auth auth
} from '../redux/outstaffingSlice' } from '../redux/outstaffingSlice'
import SVG from 'react-inlinesvg' import SVG from 'react-inlinesvg'
import { WithLogout } from '../hoc/withLogout' import {WithLogout} from '../hoc/withLogout'
import Form from '../components/Form/Form' import Form from '../components/Form/Form'
import { LEVELS, SKILLS } from '../components/constants/constants' import {LEVELS, SKILLS} from '../components/constants/constants'
import { fetchGet } from '../server/server' import {fetchGet} from '../server/server'
import { Footer } from '../components/Footer/Footer' import {Footer} from '../components/Footer/Footer'
import arrow from '../images/right-arrow.png' import arrow from '../images/right-arrow.png'
import rectangle from '../images/rectangle_secondPage.png' import rectangle from '../images/rectangle_secondPage.png'
import telegramIcon from '../images/telegram-icon.svg' import telegramIcon from '../images/telegram-icon.svg'
import './formPage.scss' import './formPage.scss'
import { getRole } from '../redux/roleSlice' import {getRole} from '../redux/roleSlice'
const goBack = (history) => {
history.goBack()
};
const FormPage = () => { const FormPage = () => {
const params = useParams(); const params = useParams();
const history = useHistory(); const navigate = useNavigate();
const dispatch = useDispatch(); const dispatch = useDispatch();
const candidate = useSelector(selectCurrentCandidate); const candidate = useSelector(selectCurrentCandidate);
const role = useSelector(getRole); const role = useSelector(getRole);
const goBack = () => {
navigate(-1)
};
if (!candidate.id) { if (!candidate.id) {
fetchGet({ fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/profile/`, link: `${process.env.REACT_APP_API_URL}/api/profile/`,
params: Number(params.id), params: Number(params.id),
history, navigate,
role, role,
logout: () => dispatch(auth(false)) logout: () => dispatch(auth(false))
}).then((el) => dispatch(currentCandidate(el))) }).then((el) => dispatch(currentCandidate(el)))
} }
return ( return (
<WithLogout> <WithLogout>
<div className='form-page'> <div className='form-page'>
<div className='form-page__back'> <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'> <div className='form-page__arrow-img'>
<img src={arrow} alt='' /> <img src={arrow} alt=''/>
</div> </div>
<div className='form-page__back-to-candidate'> <div className='form-page__back-to-candidate'>
<span>Вернуться к кандидату</span> <span>Вернуться к кандидату</span>
</div>
</div> </div>
</div> </div>
</div> <div className='form-page__candidate'>
<div className='form-page__candidate'> <div className='form-page__avatar'>
<div className='form-page__avatar'> <img src={candidate.photo} alt='candidate avatar'/>
<img src={candidate.photo} alt='candidate avatar'/> </div>
</div> <div className='form-page__candidate-info'>
<div className='form-page__candidate-info'> <div className='form-page__position'>
<div className='form-page__position'>
<span> <span>
{candidate.specification} {SKILLS[candidate.position_id]},{' '} {candidate.specification} {SKILLS[candidate.position_id]},{' '}
{LEVELS[candidate.level]} {LEVELS[candidate.level]}
</span> </span>
</div> </div>
<div className='form-page__selected'> <div className='form-page__selected'>
<img src={rectangle} alt='rectangle'/> <img src={rectangle} alt='rectangle'/>
<span>Выбранный кандидат</span> <span>Выбранный кандидат</span>
</div>
</div> </div>
</div> </div>
<div className='form-page__interview'>
<div className='form-page__form'>
<Form/>
</div>
<div className='form-page__separator'>
<div className='form-page__line'></div>
<div className='form-page__option'>или</div>
</div>
<div className='form-page__telegram'>
<div className='form-page__telegram-text'>
Заявка на собеседование через телеграм
</div>
<div className='form-page__telegram-icon'>
<a href='https://t.me/st0kir' target='_blank' rel="noreferrer">
<SVG src={telegramIcon}/>
</a>
</div>
</div>
</div>
<Footer/>
</div> </div>
<div className='form-page__interview'> </WithLogout>
<div className='form-page__form'>
<Form />
</div>
<div className='form-page__separator'>
<div className='form-page__line'></div>
<div className='form-page__option'>или</div>
</div>
<div className='form-page__telegram'>
<div className='form-page__telegram-text'>
Заявка на собеседование через телеграм
</div>
<div className='form-page__telegram-icon'>
<a href='https://t.me/st0kir' target='_blank' rel="noreferrer">
<SVG src={telegramIcon} />
</a>
</div>
</div>
</div>
<Footer />
</div>
</WithLogout>
) )
}; };

View File

@ -1,96 +0,0 @@
import React from 'react';
import {useSelector} from "react-redux";
import {getProfileInfo} from "../redux/outstaffingSlice";
import {ProfileHeader} from "../components/Profile/ProfileHeader";
import {Link} from "react-router-dom";
import {Footer} from "../components/Footer/Footer";
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 '../components/Profile/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={'/ProfileCalendar'} className='item'>
<div className='item__about'>
<img src={reportsIcon} alt='report'/>
<h3>Ваша отчетность</h3>
</div>
<div className='item__info'>
<p><span>У вас 122 часа</span><br/> отработанных в этом месяце</p>
<div className='item__infoLink'>
<img src={rightArrow} alt='arrow'/>
</div>
</div>
</Link>
<Link to={'/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>
)
};

View 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={'/calendar'} className='item'>
<div className='item__about'>
<img src={reportsIcon} alt='report'/>
<h3>Ваша отчетность</h3>
</div>
<div className='item__info'>
<p><span>У вас 122 часа</span><br/> отработанных в этом месяце</p>
<div className='item__infoLink'>
<img src={rightArrow} alt='arrow'/>
</div>
</div>
</Link>
<Link to={'/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>
)
};

View File

@ -1,16 +1,19 @@
import React, {useEffect, useState} from 'react'; import React, {useEffect, useState} from 'react';
import {ProfileHeader} from "../components/Profile/ProfileHeader"; import {ProfileHeader} from "../../components/ProfileHeader/ProfileHeader";
import {getProfileInfo} from "../redux/outstaffingSlice"; import {getProfileInfo} from "../../redux/outstaffingSlice";
import {useSelector} from "react-redux"; import {useSelector} from "react-redux";
import {transformHtml} from "../helper"; import {transformHtml} from "../../helper";
import {Footer} from '../components/Footer/Footer' import {Footer} from '../../components/Footer/Footer'
import arrow from "../../images/right-arrow.png";
import rightArrow from "../../images/arrowRight.png"
import gitImgItem from "../../images/gitItemImg.png"
import {fetchGet} from "../../server/server";
import './summary.scss'
import arrow from "../images/right-arrow.png";
import rightArrow from "../images/arrowRight.png"
import gitImgItem from "../images/gitItemImg.png"
import '../components/Profile/summary.scss'
import {fetchGet} from "../server/server";
export const Summary = () => { export const Summary = () => {
const profileInfo = useSelector(getProfileInfo); const profileInfo = useSelector(getProfileInfo);

View File

@ -1,6 +1,6 @@
import {Redirect} from "react-router-dom" import {useNavigate} from "react-router-dom"
import { HeaderPageTestsQuiz } from "../../components/features/quiz/HeaderPageTests" import {HeaderPageTestsQuiz} from "../../components/features/quiz/HeaderPageTests"
import { Instruction } from "../../components/features/quiz/Instructions" import {Instruction} from "../../components/features/quiz/Instructions"
import React from "react"; import React from "react";
import {useSelector} from "react-redux"; import {useSelector} from "react-redux";
import {selectedTest} from "../../redux/quizSlice"; import {selectedTest} from "../../redux/quizSlice";
@ -8,16 +8,17 @@ import {selectedTest} from "../../redux/quizSlice";
export const InstructionPage = () => { export const InstructionPage = () => {
const test = useSelector(selectedTest) const test = useSelector(selectedTest)
if(!test){ let navigate = useNavigate();
return <Redirect to={'/quiz'} /> if (!test) {
} navigate('/quiz')
}
return ( return (
<> <>
<HeaderPageTestsQuiz isVisibilityButton={false}/> <HeaderPageTestsQuiz isVisibilityButton={false}/>
<Instruction /> <Instruction/>
</> </>
) )
} };

View File

@ -1,24 +1,26 @@
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 {HeaderPageTestsQuiz} from "../../components/features/quiz/HeaderPageTests"
import {MyTestsQuiz} from "../../components/features/quiz/MyTestsQuiz" import {MyTestsQuiz} from "../../components/features/quiz/MyTestsQuiz"
import {useSelector} from "react-redux";
import {selectedTest, selectPassedTests} from "../../redux/quizSlice"; import {selectedTest, selectPassedTests} from "../../redux/quizSlice";
import React from "react";
export const InterjacentPage = () => { export const InterjacentPage = () => {
const test = useSelector(selectedTest) const test = useSelector(selectedTest);
const passedTests = useSelector(selectPassedTests) const passedTests = useSelector(selectPassedTests)
let navigate = useNavigate();
if (!test) {
navigate('/quiz')
}
if (!test) { return (
return <Redirect to={'/quiz'}/> <>
}
return (
<>
<HeaderPageTestsQuiz isVisibilityButton={true}/> <HeaderPageTestsQuiz isVisibilityButton={true}/>
<MyTestsQuiz listTests={passedTests}/> <MyTestsQuiz listTests={passedTests}/>
</> </>
) )
} };

View File

@ -1,4 +1,4 @@
import {Redirect} from 'react-router-dom' import {useNavigate} from 'react-router-dom'
import {HeaderPageTestsQuiz} from '../../components/features/quiz/HeaderPageTests' import {HeaderPageTestsQuiz} from '../../components/features/quiz/HeaderPageTests'
import {TaskQuiz} from '../../components/features/quiz/Task' import {TaskQuiz} from '../../components/features/quiz/Task'
import {useSelector} from "react-redux"; import {useSelector} from "react-redux";
@ -6,16 +6,16 @@ import {selectedTest} from "../../redux/quizSlice";
import React from "react"; import React from "react";
export const QuizTestPage = () => { export const QuizTestPage = () => {
let navigate = useNavigate()
const test = useSelector(selectedTest)
const test = useSelector(selectedTest) if (!test) {
navigate('/quiz')
if (!test) { }
return <Redirect to={'/quiz'}/> return (
} <>
return (
<>
<HeaderPageTestsQuiz isVisibilityButton={false}/> <HeaderPageTestsQuiz isVisibilityButton={false}/>
<TaskQuiz/> <TaskQuiz/>
</> </>
) )
}; };

View File

@ -1,4 +1,4 @@
import {Redirect} from "react-router-dom" import {useNavigate} from "react-router-dom"
import {HeaderPageTestsQuiz} from "../../components/features/quiz/HeaderPageTests" import {HeaderPageTestsQuiz} from "../../components/features/quiz/HeaderPageTests"
import {Results} from "../../components/features/quiz/Results"; import {Results} from "../../components/features/quiz/Results";
import {useSelector} from "react-redux"; import {useSelector} from "react-redux";
@ -8,16 +8,17 @@ import React from "react";
export const ResultPage = () => { export const ResultPage = () => {
const test = useSelector(selectedTest) const test = useSelector(selectedTest)
if (!test) { let navigate = useNavigate();
return <Redirect to={'/quiz'}/> if (!test) {
} navigate('/quiz')
}
return ( return (
<> <>
<HeaderPageTestsQuiz isVisibilityButton={false}/> <HeaderPageTestsQuiz isVisibilityButton={false}/>
<Results/> <Results/>
</> </>
) )
}; };

View File

@ -1,22 +1,22 @@
export const withAuthRedirect = export const withAuthRedirect =
(actionCall) => (actionCall) =>
({ link, params, history, role, logout, body }) => { ({ link, params, history, role, logout, body }) => {
const linkWithParams = params // const linkWithParams = params
? `${link}?${new URLSearchParams(params)}` // ? `${link}?${new URLSearchParams(params)}`
: link // : link;
return actionCall(linkWithParams, body) // return actionCall(linkWithParams, body)
.then((res) => { // .then((res) => {
if (res.status && res.status === 401) { // if (res.status && res.status === 401) {
localStorage.clear() // localStorage.clear();
logout && logout() // logout && logout();
history.push(role === 'ROLE_DEV' ? '/authdev' : '/auth') // history.push(role === 'ROLE_DEV' ? '/authdev' : '/auth')
} // }
//
return res // return res
}) // })
.catch((err) => { // .catch((err) => {
localStorage.clear() // localStorage.clear();
logout && logout() // logout && logout();
history.push(role === 'ROLE_DEV' ? '/authdev' : '/auth') // history.push(role === 'ROLE_DEV' ? '/authdev' : '/auth')
}) // })
} };

View File

@ -19,67 +19,67 @@ import { withAuthRedirect } from './authRedirect'
// } // }
// }); // });
export const fetchAuth = async ({ // export const fetchAuth = async ({
username, // username,
password, // password,
dispatch, // dispatch,
catchError // catchError
}) => { // }) => {
const apiURL = process.env.REACT_APP_API_URL; // const apiURL = process.env.REACT_APP_API_URL;
try { // try {
const response = await fetch(`${apiURL}/api/user/login`, { // const response = await fetch(`${apiURL}/api/user/login`, {
method: 'POST', // method: 'POST',
mode: 'cors', // mode: 'cors',
headers: { // headers: {
'Access-Control-Request-Headers': 'authorization', // 'Access-Control-Request-Headers': 'authorization',
'Content-Type': 'application/json' // 'Content-Type': 'application/json'
// Origin: `http://localhost` // // Origin: `http://localhost`
}, // },
body: JSON.stringify({ // body: JSON.stringify({
username, // username,
password // 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('cardId', resJSON.card_id);
// localStorage.setItem(
// 'access_token_expired_at',
// resJSON.access_token_expired_at
// );
// dispatch(resJSON)
// })
// } catch (error) {
// console.error('Error occured: ', error)
// }
// };
if (!response.ok) { // export const fetchReportList = withAuthRedirect(async (link) => {
catchError(); // try {
return response.statusText // const response = await fetch(
} // `https://guild.loc/api/reports/index?user_id=26&fromDate=2021-10-18`,
// // link,
response.json().then((resJSON) => { // {
localStorage.setItem('auth_token', resJSON.access_token); // method: 'GET',
localStorage.setItem('id', resJSON.id); // headers: {
localStorage.setItem('cardId', resJSON.card_id); // Authorization: `Bearer ${localStorage.getItem('auth_token')}`
localStorage.setItem( // }
'access_token_expired_at', // }
resJSON.access_token_expired_at // );
); // let data = await response.json();
dispatch(resJSON) //
}) // return data
} catch (error) { // } catch (error) {
console.error('Error occured: ', error) // console.log('Query error', 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) => { export const fetchGet = withAuthRedirect(async (link) => {
try { try {