Переписываю спорные решения
This commit is contained in:
		| @@ -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 | ||||
							
								
								
									
										39090
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										39090
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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", | ||||
|   | ||||
							
								
								
									
										106
									
								
								src/App.js
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								src/App.js
									
									
									
									
									
								
							| @@ -1,12 +1,9 @@ | ||||
| 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' | ||||
| @@ -14,59 +11,56 @@ 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 { Profile } from './pages/Profile.js' | ||||
| import { Summary } from './pages/Summary' | ||||
| 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 /> | ||||
|           </Route> | ||||
|           <Route path='/auth' exact> | ||||
|             <AuthPageForPartners /> | ||||
|           </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='/ProfileCalendar' component={ProfileCalendarPage} /> | ||||
|           <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='/profile' component={Profile} /> | ||||
|           <ProtectedRoute path='/summary' component={Summary} /> | ||||
|           <ProtectedRoute path='/quiz-result' component={ResultPage} /> | ||||
|           <ProtectedRoute component={() => <div>Page not found</div>} /> | ||||
|         </Switch> | ||||
|       </Router> | ||||
|     </> | ||||
|       <> | ||||
|         <h1>IT Аутстаффинг в России</h1> | ||||
|         <Router> | ||||
|  | ||||
|           <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 path='/quiz' element={<QuizPage/>}/> | ||||
|             <Route path='/quiz-interjacent' element={<InterjacentPage/>}/> | ||||
|             <Route path='/quiz-test' element={<QuizTestPage/>}/> | ||||
|             <Route path='/quiz-instruction' element={<InstructionPage/>}/> | ||||
|             <Route path='/quiz-result' element={<ResultPage/>}/> | ||||
|  | ||||
|             <Route exact path='/profile' element={<Profile/>}> | ||||
|               <Route exact path='calendar' element={<ProfileCalendarPage/>}/> | ||||
|               <Route exact path='summary' element={<Summary/>}/> | ||||
|             </Route> | ||||
|  | ||||
|             {/*<Route component={() => <div>Page not found</div>} />*/} | ||||
|             {/*<Route path='*' component={() => <div>Page not found</div>}/>*/} | ||||
|  | ||||
|             <Route path="*" element={<Navigate to="/" replace/>}/> | ||||
|           </Routes> | ||||
|         </Router> | ||||
|       </> | ||||
|   ) | ||||
| } | ||||
| }; | ||||
|  | ||||
| export default App | ||||
|   | ||||
							
								
								
									
										48
									
								
								src/api/request.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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; | ||||
| @@ -1,107 +1,129 @@ | ||||
| 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'> | ||||
|       <h2 className='auth-box__header'> | ||||
|         Войти в <span>систему</span> | ||||
|       </h2> | ||||
|       <div className='auth-box__title'> | ||||
|         <img src={ellipse} alt='' /> | ||||
|         <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 className='auth-box'> | ||||
|         <h2 className='auth-box__header'> | ||||
|           Войти в <span>систему</span> | ||||
|         </h2> | ||||
|         <div className='auth-box__title'> | ||||
|           <img src={ellipse} alt=''/> | ||||
|           <span>{title}</span> | ||||
|         </div> | ||||
|       </form> | ||||
|     </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={(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> | ||||
|   ) | ||||
| } | ||||
| }; | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| 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 { | ||||
|   currentCandidate, | ||||
| @@ -22,8 +22,8 @@ import {Footer} from '../Footer/Footer' | ||||
| import './candidate.scss' | ||||
|  | ||||
| const Candidate = () => { | ||||
|   const history = useHistory(); | ||||
|   const {id: candidateId} = useParams(); | ||||
|   const navigate = useNavigate(); | ||||
|   const dispatch = useDispatch(); | ||||
|   const role = useSelector(getRole); | ||||
|   const [activeSnippet, setActiveSnippet] = useState(true); | ||||
| @@ -36,7 +36,6 @@ const Candidate = () => { | ||||
|     fetchGet({ | ||||
|       link: `${process.env.REACT_APP_API_URL}/api/profile/${candidateId}`, | ||||
|       params: Number(candidateId), | ||||
|       history, | ||||
|       role, | ||||
|       logout: () => dispatch(auth(false)) | ||||
|     }).then((el) => dispatch(currentCandidate(el))) | ||||
| @@ -97,7 +96,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> | ||||
|   | ||||
| @@ -1,43 +1,42 @@ | ||||
| import React, { useEffect, useState } from 'react' | ||||
| import male from '../../images/medium_male.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 { | ||||
|   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' | ||||
| import {useRequest} from "../../hooks/useRequest"; | ||||
|  | ||||
| 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([]) | ||||
|   const dispatch = useDispatch(); | ||||
|   const {apiRequest} = useRequest(); | ||||
|   const [isLoaded, setIsLoaded] = useState(false); | ||||
|   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) | ||||
|     }) | ||||
|   }, []) | ||||
|   // useEffect(() => { | ||||
|   //   apiRequest('/profile?limit=',{ | ||||
|   //     params: 1000, | ||||
|   // | ||||
|   //     role, | ||||
|   //     logout: () => dispatch(auth(false)) | ||||
|     // }).then((p) => { | ||||
|     //   getAllCandidates(p); | ||||
|     //   setIsLoaded(true) | ||||
|     // }) | ||||
|   // }, []); | ||||
|  | ||||
|   if (!filteredListArr) { | ||||
|     return ( | ||||
| @@ -242,6 +241,6 @@ const Description = ({ onLoadMore, isLoadingMore }) => { | ||||
|       </div> | ||||
|     </section> | ||||
|   ) | ||||
| } | ||||
| }; | ||||
|  | ||||
| export default Description | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| 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' | ||||
| @@ -16,7 +16,7 @@ const SweetAlert = withSwalInstance(swal) | ||||
|  | ||||
| const Form = () => { | ||||
|   const dispatch = useDispatch() | ||||
|   const history = useHistory() | ||||
|   const navigate = useNavigate() | ||||
|   const role = useSelector(getRole) | ||||
|   const urlParams = useParams() | ||||
|   const [status, setStatus] = useState(null) | ||||
| @@ -52,7 +52,7 @@ const Form = () => { | ||||
|         profile_id: urlParams.id, | ||||
|         ...data | ||||
|       }, | ||||
|       history, | ||||
|  | ||||
|       role, | ||||
|       logout: () => dispatch(auth(false)) | ||||
|     }).then((res) => | ||||
| @@ -79,8 +79,8 @@ const Form = () => { | ||||
|                   setStatus(null) | ||||
|                 } | ||||
|               : () => { | ||||
|                   setStatus(null) | ||||
|                   history.push(`/candidate/${urlParams.id}`) | ||||
|                   setStatus(null); | ||||
|                   navigate(`/candidate/${urlParams.id}`) | ||||
|                 } | ||||
|           } | ||||
|         /> | ||||
|   | ||||
| @@ -1,66 +1,72 @@ | ||||
| import React, { useState, useEffect } from 'react' | ||||
| import { useDispatch, useSelector } from 'react-redux' | ||||
| import React, {useState, useEffect} from 'react' | ||||
| import {useDispatch, useSelector} 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, auth} from '../../redux/outstaffingSlice' | ||||
| import {getRole} from '../../redux/roleSlice' | ||||
|  | ||||
| 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 role = useSelector(getRole); | ||||
|  | ||||
|   const {apiRequest} = useRequest(); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     setIsLoadingMore(true) | ||||
|     fetchGet({ | ||||
|       link: `${process.env.REACT_APP_API_URL}/api/profile?limit=`, | ||||
|       params: index, | ||||
|       history, | ||||
|     setIsLoadingMore(true); | ||||
|     apiRequest('/profile',{ | ||||
|       //Корс блокирует все фильтры в гет параметрах | ||||
|       // params: {"get-document-list": 1000}, | ||||
|       role, | ||||
|       logout: () => dispatch(auth(false)) | ||||
|       // logout: () => dispatch(auth(false)) | ||||
|     }).then((profileArr) => { | ||||
|       dispatch(profiles(profileArr)) | ||||
|       setIsLoadingMore(false) | ||||
|     }) | ||||
|  | ||||
|     fetchGet({ | ||||
|       link: `${process.env.REACT_APP_API_URL}/api/skills/skills-on-main-page`, | ||||
|       history, | ||||
|       dispatch(profiles(profileArr)); | ||||
|       setIsLoadingMore(false) | ||||
|     }); | ||||
|  | ||||
|     apiRequest('/skills/skills-on-main-page',{ | ||||
|  | ||||
|       role, | ||||
|       logout: () => dispatch(auth(false)) | ||||
|       // logout: () => dispatch(auth(false)) | ||||
|     }).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] } | ||||
|         }) | ||||
|       ) | ||||
|           value.map((val) => { | ||||
|             return {id: val.id, value: val.tags, name: keys[index]} | ||||
|           }) | ||||
|       ); | ||||
|       dispatch(tags(tempTags)) | ||||
|     }) | ||||
|   }, [dispatch, index]) | ||||
|   }, [dispatch, 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> | ||||
|   ) | ||||
| } | ||||
| }; | ||||
|   | ||||
| @@ -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/> : 'Выйти'}{' '} | ||||
|   | ||||
| @@ -21,18 +21,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 +91,6 @@ const Outstaffing = () => { | ||||
|       <TagSelect /> | ||||
|     </> | ||||
|   ) | ||||
| } | ||||
| }; | ||||
|  | ||||
| export default Outstaffing | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import { | ||||
| } from '../../redux/outstaffingSlice' | ||||
|  | ||||
| import { fetchGet } from '../../server/server' | ||||
| import { useHistory } from 'react-router-dom' | ||||
|  | ||||
| import { getRole } from '../../redux/roleSlice' | ||||
|  | ||||
| import './outstaffingBlock.scss' | ||||
| @@ -19,35 +19,32 @@ const handlePositionClick = ({ | ||||
|   positionId, | ||||
|   isSelected, | ||||
|   onSelect, | ||||
|   history, | ||||
|   role | ||||
| }) => { | ||||
|   if (isSelected) { | ||||
|     fetchGet({ | ||||
|       link: `${process.env.REACT_APP_API_URL}/api/profile?limit=`, | ||||
|       params: 4, | ||||
|       history, | ||||
|       role, | ||||
|       logout: () => dispatch(auth(false)) | ||||
|     }).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)) | ||||
|     }).then((el) => { | ||||
|       dispatch(filteredCandidates(el)) | ||||
|       dispatch(selectedItems([])) | ||||
|       dispatch(filteredCandidates(el)); | ||||
|       dispatch(selectedItems([])); | ||||
|       onSelect(positionId) | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| }; | ||||
|  | ||||
| const OutstaffingBlock = ({ | ||||
|   dataTags = [], | ||||
| @@ -58,20 +55,20 @@ const OutstaffingBlock = ({ | ||||
|   isSelected, | ||||
|   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) => { | ||||
|     if (!itemsArr.find((el) => item === el.value)) { | ||||
|       dispatch(selectedItems([...itemsArr, { id, value: item, label: item }])) | ||||
|     } | ||||
|   } | ||||
|   }; | ||||
|  | ||||
|   let classes | ||||
|   let classes; | ||||
|  | ||||
|   dataTags.forEach((el) => { | ||||
|     if (el.name === 'skills_back') { | ||||
| @@ -81,7 +78,7 @@ const OutstaffingBlock = ({ | ||||
|     } else if (el.name === 'skills_front') { | ||||
|       classes = 'front' | ||||
|     } | ||||
|   }) | ||||
|   }); | ||||
|  | ||||
|   return ( | ||||
|     <OutsideClickHandler | ||||
| @@ -104,7 +101,6 @@ const OutstaffingBlock = ({ | ||||
|               positionId, | ||||
|               isSelected, | ||||
|               onSelect, | ||||
|               history, | ||||
|               role | ||||
|             }) | ||||
|           } | ||||
| @@ -136,6 +132,6 @@ const OutstaffingBlock = ({ | ||||
|       </div> | ||||
|     </OutsideClickHandler> | ||||
|   ) | ||||
| } | ||||
| }; | ||||
|  | ||||
| export default OutstaffingBlock | ||||
|   | ||||
| @@ -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> | ||||
|     ) | ||||
| } | ||||
							
								
								
									
										71
									
								
								src/components/ProfileHeader/ProfileHeader.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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(`/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> | ||||
|   ) | ||||
| }; | ||||
| @@ -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,7 +1,7 @@ | ||||
| import React, {useState} from 'react' | ||||
| import {useSelector, useDispatch} from 'react-redux' | ||||
| 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 {auth} from '../../redux/outstaffingSlice' | ||||
| import {getReportDate} from '../../redux/reportSlice' | ||||
| @@ -28,7 +28,7 @@ const getCreatedDate = (day) => { | ||||
|  | ||||
| const ReportForm = () => { | ||||
|   const dispatch = useDispatch(); | ||||
|   const history = useHistory(); | ||||
|   const navigate = useNavigate(); | ||||
|   const reportDate = useSelector(getReportDate); | ||||
|   const role = useSelector(getRole); | ||||
|  | ||||
| @@ -153,7 +153,6 @@ const ReportForm = () => { | ||||
|               <button className='report-form__footer-btn' onClick={() => { | ||||
|                 fetchPost({ | ||||
|                   link: `${process.env.REACT_APP_API_URL}/api/reports/create`, | ||||
|                   history, | ||||
|                   role, | ||||
|                   body: { | ||||
|                     tasks: inputs, | ||||
|   | ||||
| @@ -12,38 +12,37 @@ import { | ||||
|   auth | ||||
| } from '../../redux/outstaffingSlice' | ||||
| import { fetchGet } from '../../server/server' | ||||
| import { useHistory } from 'react-router-dom' | ||||
|  | ||||
| import { getRole } from '../../redux/roleSlice' | ||||
|  | ||||
| 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 }) => { | ||||
|     setSearchLoading(true) | ||||
|     setSearchLoading(true); | ||||
|  | ||||
|     dispatch(setPositionId(null)) | ||||
|     const filterItemsId = itemsArr.map((item) => item.id).join() | ||||
|     dispatch(setPositionId(null)); | ||||
|     const filterItemsId = itemsArr.map((item) => item.id).join(); | ||||
|  | ||||
|     fetchGet({ | ||||
|       link: `${process.env.REACT_APP_API_URL}/api/profile?skills=`, | ||||
|       params: filterItemsId, | ||||
|       history, | ||||
|       role, | ||||
|       logout: () => dispatch(auth(false)) | ||||
|     }).then((el) => { | ||||
|       dispatch(filteredCandidates(el)) | ||||
|       dispatch(filteredCandidates(el)); | ||||
|       setSearchLoading(false) | ||||
|     }) | ||||
|  | ||||
|     // dispatch(selectedItems([])); | ||||
|   } | ||||
|   }; | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
| @@ -85,6 +84,6 @@ const TagSelect = () => { | ||||
|       </section> | ||||
|     </> | ||||
|   ) | ||||
| } | ||||
| }; | ||||
|  | ||||
| export default TagSelect | ||||
|   | ||||
| @@ -5,20 +5,20 @@ import {fetchGet} from "../../../server/server"; | ||||
|  | ||||
|  | ||||
| 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(); | ||||
|  | ||||
|    useEffect(async () => { | ||||
|       dispatch(fetchResultTest(test.uuid)) | ||||
|       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) | ||||
|    }, []) | ||||
|    }, []); | ||||
|  | ||||
|    return ( | ||||
|      <div className={'result _container'}> | ||||
|   | ||||
| @@ -1,59 +1,62 @@ | ||||
| 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 React, {useEffect, useState} from 'react' | ||||
| import { useNavigate} from "react-router-dom" | ||||
| import {useSelector, useDispatch} from 'react-redux' | ||||
|  | ||||
| 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 {fetchUserAnswersMany, fetchUserAnswerOne} from './../../../redux/quizSlice' | ||||
|  | ||||
| import {fetchGet} from "../../../server/server"; | ||||
|  | ||||
| import './quiz.scss' | ||||
|  | ||||
| export const TaskQuiz = () => { | ||||
|  | ||||
|    const history = useHistory(); | ||||
|    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([]) | ||||
|    const navigate = useNavigate(); | ||||
|    const dispatch = useDispatch(); | ||||
|  | ||||
|    useEffect(async () => { | ||||
|       const response = await fetchGet({ | ||||
|    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 [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}`, | ||||
|            Origin: `${process.env.REACT_APP_BASE_URL}`, | ||||
|         } | ||||
|       ) | ||||
|       setQuestions(response) | ||||
|       dispatch(fetchGetAnswers(response[0].id)) | ||||
|       ); | ||||
|       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 +64,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 +89,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': | ||||
| @@ -146,4 +149,4 @@ export const TaskQuiz = () => { | ||||
|         </div> | ||||
|      </React.StrictMode> | ||||
|    ) | ||||
| } | ||||
| }; | ||||
|   | ||||
| @@ -18,3 +18,20 @@ export function transformHtml(text) { | ||||
|   ); | ||||
|   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
									
								
							
							
						
						
									
										20
									
								
								src/hooks/useLogout.js
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										64
									
								
								src/hooks/useRequest.js
									
									
									
									
									
										Normal 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} | ||||
| }; | ||||
| @@ -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='/profile' /> | ||||
|     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,98 +1,99 @@ | ||||
| import React from 'react' | ||||
| import { useDispatch, useSelector } from 'react-redux' | ||||
| import { useHistory, useParams } from 'react-router-dom' | ||||
| 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 '../components/constants/constants' | ||||
| import {fetchGet} from '../server/server' | ||||
| 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 {getRole} from '../redux/roleSlice' | ||||
|  | ||||
| const goBack = (history) => { | ||||
|   history.goBack() | ||||
| }; | ||||
|  | ||||
| const FormPage = () => { | ||||
|   const params = useParams(); | ||||
|   const history = useHistory(); | ||||
|   const navigate = useNavigate(); | ||||
|   const dispatch = useDispatch(); | ||||
|   const candidate = useSelector(selectCurrentCandidate); | ||||
|   const role = useSelector(getRole); | ||||
|  | ||||
|   const goBack = () => { | ||||
|     navigate(-1) | ||||
|   }; | ||||
|  | ||||
|   if (!candidate.id) { | ||||
|     fetchGet({ | ||||
|       link: `${process.env.REACT_APP_API_URL}/api/profile/`, | ||||
|       params: Number(params.id), | ||||
|       history, | ||||
|       navigate, | ||||
|       role, | ||||
|       logout: () => dispatch(auth(false)) | ||||
|     }).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-img'> | ||||
|               <img src={arrow} alt='' /> | ||||
|             </div> | ||||
|             <div className='form-page__back-to-candidate'> | ||||
|               <span>Вернуться к кандидату</span> | ||||
|       <WithLogout> | ||||
|         <div className='form-page'> | ||||
|           <div className='form-page__back'> | ||||
|             <div className='form-page__arrow' onClick={goBack}> | ||||
|               <div className='form-page__arrow-img'> | ||||
|                 <img src={arrow} alt=''/> | ||||
|               </div> | ||||
|               <div className='form-page__back-to-candidate'> | ||||
|                 <span>Вернуться к кандидату</span> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div className='form-page__candidate'> | ||||
|           <div className='form-page__avatar'> | ||||
|             <img src={candidate.photo} alt='candidate avatar'/> | ||||
|           </div> | ||||
|           <div className='form-page__candidate-info'> | ||||
|             <div className='form-page__position'> | ||||
|           <div className='form-page__candidate'> | ||||
|             <div className='form-page__avatar'> | ||||
|               <img src={candidate.photo} alt='candidate avatar'/> | ||||
|             </div> | ||||
|             <div className='form-page__candidate-info'> | ||||
|               <div className='form-page__position'> | ||||
|               <span> | ||||
|                 {candidate.specification} {SKILLS[candidate.position_id]},{' '} | ||||
|                 {LEVELS[candidate.level]} | ||||
|               </span> | ||||
|             </div> | ||||
|             <div className='form-page__selected'> | ||||
|               <img src={rectangle} alt='rectangle'/> | ||||
|               <span>Выбранный кандидат</span> | ||||
|               </div> | ||||
|               <div className='form-page__selected'> | ||||
|                 <img src={rectangle} alt='rectangle'/> | ||||
|                 <span>Выбранный кандидат</span> | ||||
|               </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 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> | ||||
|     </WithLogout> | ||||
|       </WithLogout> | ||||
|   ) | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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> | ||||
|     ) | ||||
| }; | ||||
							
								
								
									
										100
									
								
								src/pages/Profile/Profile.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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={'/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> | ||||
|   ) | ||||
| }; | ||||
| @@ -1,16 +1,19 @@ | ||||
| import React, {useEffect, useState} from 'react'; | ||||
| import {ProfileHeader} from "../components/Profile/ProfileHeader"; | ||||
| import {getProfileInfo} from "../redux/outstaffingSlice"; | ||||
| import {ProfileHeader} from "../../components/ProfileHeader/ProfileHeader"; | ||||
| import {getProfileInfo} from "../../redux/outstaffingSlice"; | ||||
| import {useSelector} from "react-redux"; | ||||
| import {transformHtml} from "../helper"; | ||||
| import {Footer} from '../components/Footer/Footer' | ||||
| import {transformHtml} from "../../helper"; | ||||
| 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 = () => { | ||||
|     const profileInfo = useSelector(getProfileInfo); | ||||
| @@ -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"; | ||||
| @@ -8,16 +8,17 @@ import {selectedTest} from "../../redux/quizSlice"; | ||||
|  | ||||
| export const InstructionPage = () => { | ||||
|  | ||||
|     const test = useSelector(selectedTest) | ||||
|   const test = useSelector(selectedTest) | ||||
|  | ||||
|     if(!test){ | ||||
|         return <Redirect to={'/quiz'} /> | ||||
|     } | ||||
|   let navigate = useNavigate(); | ||||
|   if (!test) { | ||||
|     navigate('/quiz') | ||||
|   } | ||||
|  | ||||
|     return ( | ||||
|         <> | ||||
|             <HeaderPageTestsQuiz isVisibilityButton={false}/> | ||||
|             <Instruction /> | ||||
|         </> | ||||
|     ) | ||||
| } | ||||
|   return ( | ||||
|       <> | ||||
|         <HeaderPageTestsQuiz isVisibilityButton={false}/> | ||||
|         <Instruction/> | ||||
|       </> | ||||
|   ) | ||||
| }; | ||||
| @@ -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 {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 passedTests = useSelector(selectPassedTests) | ||||
|   const test = useSelector(selectedTest); | ||||
|   const passedTests = useSelector(selectPassedTests) | ||||
|   let navigate = useNavigate(); | ||||
|   if (!test) { | ||||
|      navigate('/quiz') | ||||
|   } | ||||
|  | ||||
|    if (!test) { | ||||
|       return <Redirect to={'/quiz'}/> | ||||
|    } | ||||
|  | ||||
|    return ( | ||||
|      <> | ||||
|   return ( | ||||
|       <> | ||||
|         <HeaderPageTestsQuiz isVisibilityButton={true}/> | ||||
|         <MyTestsQuiz listTests={passedTests}/> | ||||
|      </> | ||||
|    ) | ||||
| } | ||||
|       </> | ||||
|   ) | ||||
| }; | ||||
| @@ -1,4 +1,4 @@ | ||||
| import {Redirect} from 'react-router-dom' | ||||
| import {useNavigate} from 'react-router-dom' | ||||
| import {HeaderPageTestsQuiz} from '../../components/features/quiz/HeaderPageTests' | ||||
| import {TaskQuiz} from '../../components/features/quiz/Task' | ||||
| import {useSelector} from "react-redux"; | ||||
| @@ -6,16 +6,16 @@ import {selectedTest} from "../../redux/quizSlice"; | ||||
| import React from "react"; | ||||
|  | ||||
| export const QuizTestPage = () => { | ||||
|   let navigate = useNavigate() | ||||
|   const test = useSelector(selectedTest) | ||||
|  | ||||
|    const test = useSelector(selectedTest) | ||||
|  | ||||
|    if (!test) { | ||||
|       return <Redirect to={'/quiz'}/> | ||||
|    } | ||||
|    return ( | ||||
|      <> | ||||
|   if (!test) { | ||||
|      navigate('/quiz') | ||||
|   } | ||||
|   return ( | ||||
|       <> | ||||
|         <HeaderPageTestsQuiz isVisibilityButton={false}/> | ||||
|         <TaskQuiz/> | ||||
|      </> | ||||
|    ) | ||||
|       </> | ||||
|   ) | ||||
| }; | ||||
| @@ -1,4 +1,4 @@ | ||||
| import {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"; | ||||
| @@ -8,16 +8,17 @@ import React from "react"; | ||||
|  | ||||
| export const ResultPage = () => { | ||||
|  | ||||
|    const test = useSelector(selectedTest) | ||||
|   const test = useSelector(selectedTest) | ||||
|  | ||||
|    if (!test) { | ||||
|       return <Redirect to={'/quiz'}/> | ||||
|    } | ||||
|   let navigate = useNavigate(); | ||||
|   if (!test) { | ||||
|     navigate('/quiz') | ||||
|   } | ||||
|  | ||||
|    return ( | ||||
|      <> | ||||
|   return ( | ||||
|       <> | ||||
|         <HeaderPageTestsQuiz isVisibilityButton={false}/> | ||||
|         <Results/> | ||||
|      </> | ||||
|    ) | ||||
|       </> | ||||
|   ) | ||||
| }; | ||||
| @@ -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') | ||||
|       // }) | ||||
|   }; | ||||
|   | ||||
| @@ -19,67 +19,67 @@ import { withAuthRedirect } from './authRedirect' | ||||
| //   } | ||||
| // }); | ||||
|  | ||||
| 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 | ||||
|       }) | ||||
|     }); | ||||
| // 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('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) { | ||||
|       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) | ||||
|   } | ||||
| }; | ||||
|  | ||||
| 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 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 { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user