commit
ae9a2efd37
package-lock.jsonpackage.json
src
App.js
assets
components
CardControl
CategoriesItem
Footer
Navigation
ProfileCalendar
ProfileHeader
ReportForm
StarRating
features
images
index.csspages
AuthForCandidate
Home
PartnerAddRequest
PartnerBid
PartnerRequests
PartnerSettings
PartnerTreaties
PartnerСategories
Payouts
Profile
ProfileCandidate
Summary
Tracker
ViewReport
quiz
redux
10
package-lock.json
generated
10
package-lock.json
generated
@ -48,6 +48,7 @@
|
||||
"react-select": "^5.7.0",
|
||||
"react-slick": "^0.29.0",
|
||||
"react-syntax-highlighter": "^15.4.5",
|
||||
"react-timer-hook": "^3.0.5",
|
||||
"react-yandex-metrika": "^2.6.0",
|
||||
"redux-devtools-extension": "^2.13.9",
|
||||
"resolve": "1.18.1",
|
||||
@ -21571,6 +21572,15 @@
|
||||
"react": ">= 0.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-timer-hook": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/react-timer-hook/-/react-timer-hook-3.0.5.tgz",
|
||||
"integrity": "sha512-n+98SdmYvui2ne3KyWb3Ldu4k0NYQa3g/VzW6VEIfZJ8GAk/jJsIY700M8Nd2vNSTj05c7wKyQfJBqZ0x7zfiA==",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-transition-group": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||
|
@ -44,6 +44,7 @@
|
||||
"react-select": "^5.7.0",
|
||||
"react-slick": "^0.29.0",
|
||||
"react-syntax-highlighter": "^15.4.5",
|
||||
"react-timer-hook": "^3.0.5",
|
||||
"react-yandex-metrika": "^2.6.0",
|
||||
"redux-devtools-extension": "^2.13.9",
|
||||
"resolve": "1.18.1",
|
||||
|
18
src/App.js
18
src/App.js
@ -13,10 +13,7 @@ import { ProfileCalendar } from "./components/ProfileCalendar/ProfileCalendar";
|
||||
import FormPage from "./pages/FormPage/FormPage.js";
|
||||
import SingleReportPage from "./pages/SingleReportPage/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 {QuizReportPage} from './pages/quiz/QuizReportPage';
|
||||
import { Profile } from "./pages/Profile/Profile.js";
|
||||
import { Summary } from "./pages/Summary/Summary";
|
||||
import { ViewReport } from "./pages/ViewReport/ViewReport";
|
||||
@ -32,9 +29,11 @@ import { PartnerEmployees } from "./pages/PartnerEmployees/PartnerEmployees";
|
||||
import {AuthForCandidate} from "./pages/AuthForCandidate/AuthForCandidate";
|
||||
import {RegistrationForCandidate} from "./pages/RegistrationForCandidate/RegistrationForCandidate";
|
||||
|
||||
|
||||
import './assets/global.scss'
|
||||
import './fonts/stylesheet.css'
|
||||
import 'bootstrap/dist/css/bootstrap.min.css'
|
||||
import { ProfileCandidate } from './pages/ProfileCandidate/ProfileCandidate';
|
||||
import { PassingTests } from './pages/quiz/PassingTests';
|
||||
|
||||
|
||||
|
||||
@ -61,10 +60,8 @@ const App = () => {
|
||||
|
||||
<Route exact path='quiz'>
|
||||
<Route index element={<QuizPage/>}/>
|
||||
<Route exact path='interjacent' element={<InterjacentPage/>}/>
|
||||
<Route exact path='test' element={<QuizTestPage/>}/>
|
||||
<Route exact path='instruction' element={<InstructionPage/>}/>
|
||||
<Route exact path='result' element={<ResultPage/>}/>
|
||||
<Route exact path='test' element={<PassingTests />}/>
|
||||
<Route exact path='report' element={<QuizReportPage/>}/>
|
||||
</Route>
|
||||
|
||||
<Route exact path='profile'>
|
||||
@ -83,6 +80,9 @@ const App = () => {
|
||||
<Route exact path='treaties' element={<PartnerTreaties/>}/>
|
||||
<Route exact path='categories/employees' element={<PartnerEmployees/>}/>
|
||||
</Route>
|
||||
<Route exact path='profile-candidate/:id'>
|
||||
<Route index element={<ProfileCandidate />}/>
|
||||
</Route>
|
||||
|
||||
<Route path="*" element={<Navigate to="/auth" replace/>}/>
|
||||
</Routes>
|
||||
|
25
src/assets/global.scss
Normal file
25
src/assets/global.scss
Normal file
@ -0,0 +1,25 @@
|
||||
a:hover {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.main-title {
|
||||
font-weight: 700;
|
||||
font-size: 22px;
|
||||
line-height: 145%;
|
||||
color: #000000;
|
||||
word-break: break-word;
|
||||
span {
|
||||
color: #52b709;
|
||||
}
|
||||
}
|
||||
.btn-green {
|
||||
background: #52b709;
|
||||
border-radius: 44px;
|
||||
width: 100%;
|
||||
border: none;
|
||||
font-weight: 400;
|
||||
line-height: 178%;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
20
src/components/CardControl/CardControl.js
Normal file
20
src/components/CardControl/CardControl.js
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react'
|
||||
import rightArrow from "../../images/arrowRight.png"
|
||||
import { Link } from 'react-router-dom'
|
||||
import './CardControl.scss'
|
||||
|
||||
export const CardControl = ({title,path,description, img}) => {
|
||||
return (
|
||||
<Link to={`/${path}`} className='control-card'>
|
||||
<div className='control-card__about'>
|
||||
<img src={img} alt='itemImg' />
|
||||
<h3>{title}</h3>
|
||||
</div>
|
||||
<div className='control-card__info'>
|
||||
<p dangerouslySetInnerHTML={{ __html: description }}></p>
|
||||
<div className='control-card__infoLink'>
|
||||
<img src={rightArrow} alt='arrow' />
|
||||
</div>
|
||||
</div>
|
||||
</Link>)
|
||||
}
|
73
src/components/CardControl/CardControl.scss
Normal file
73
src/components/CardControl/CardControl.scss
Normal file
@ -0,0 +1,73 @@
|
||||
.control-card{
|
||||
max-width: 353px;
|
||||
width: 100%;
|
||||
padding: 35px 45px 15px 30px;
|
||||
background: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 6px 5px 20px rgb(87 98 80 / 21%);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
@media (max-width: 1175px) {
|
||||
width: 48%;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
@media (max-width: 925px) {
|
||||
width: 100%;
|
||||
padding: 15px 25px;
|
||||
}
|
||||
|
||||
&__about {
|
||||
display: flex;
|
||||
column-gap: 20px;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
|
||||
@media (max-width: 925px) {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #000000;
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
max-width: 125px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
p {
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
color: #000000;
|
||||
margin-bottom: 0;
|
||||
|
||||
span {
|
||||
color: #52B709;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
&Link {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: #DDEEC6;
|
||||
border-radius: 50px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 33px 32px 25px 28px;
|
||||
width: 32%;
|
||||
background: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
transition: all 0.3s ease;
|
||||
|
@ -1,5 +1,5 @@
|
||||
footer {
|
||||
border-top: 1px solid #EBEBEB;
|
||||
border-top: 1px solid #ebebeb;
|
||||
padding: 35px 0 50px;
|
||||
}
|
||||
|
||||
|
103
src/components/Navigation/Navigation.js
Normal file
103
src/components/Navigation/Navigation.js
Normal file
@ -0,0 +1,103 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { urlForLocal } from '../../helper'
|
||||
import { apiRequest } from '../../api/request';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { getProfileInfo, setProfileInfo } from '../../redux/outstaffingSlice';
|
||||
|
||||
import avatarMok from "../../pages/PartnerTreaties/Images/avatarMok.png"
|
||||
|
||||
export const Navigation = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
|
||||
const profileInfo = useSelector(getProfileInfo);
|
||||
const [user] = useState(localStorage.getItem('role_status') === '18' ? 'partner' : 'developer')
|
||||
const [navInfo] = useState({
|
||||
developer: [
|
||||
{
|
||||
path: '/summary',
|
||||
name: 'Резюме'
|
||||
},
|
||||
{
|
||||
path: '/calendar',
|
||||
name: 'Отчетность'
|
||||
},
|
||||
{
|
||||
path: '/tracker',
|
||||
name: 'Трекер'
|
||||
},
|
||||
{
|
||||
path: '/payouts',
|
||||
name: 'Выплаты'
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
name: 'Настройки'
|
||||
},
|
||||
],
|
||||
partner: [
|
||||
{
|
||||
path: '/catalog',
|
||||
name: 'Каталог'
|
||||
},
|
||||
{
|
||||
path: '/requests',
|
||||
name: 'Запросы'
|
||||
},
|
||||
{
|
||||
path: '/categories',
|
||||
name: 'Персонал'
|
||||
},
|
||||
{
|
||||
path: '/tracker',
|
||||
name: 'Трекер'
|
||||
},
|
||||
{
|
||||
path: '/treaties',
|
||||
name: 'Договора'
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
name: 'Настройки'
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (localStorage.getItem('role_status') === '18') {
|
||||
return
|
||||
}
|
||||
apiRequest(`/profile/${localStorage.getItem('cardId')}`)
|
||||
.then((profileInfo) =>
|
||||
dispatch(setProfileInfo(profileInfo))
|
||||
);
|
||||
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<div className='profileHeader__info'>
|
||||
<div className='profileHeader__container'>
|
||||
<nav className='profileHeader__nav'>
|
||||
{
|
||||
navInfo[user].map((link, index) => {
|
||||
return <NavLink key={index} end to={`/profile${link.path}`}>{link.name}</NavLink>
|
||||
})
|
||||
}
|
||||
</nav>
|
||||
|
||||
<div className='profileHeader__personalInfo'>
|
||||
<h3 className='profileHeader__personalInfoName'>
|
||||
{user === 'developer' ?
|
||||
profileInfo?.fio :
|
||||
''
|
||||
}
|
||||
</h3>
|
||||
<NavLink end to={'/profile'}>
|
||||
<img src={profileInfo.photo ? urlForLocal(profileInfo.photo) : avatarMok} className='profileHeader__personalInfoAvatar' alt='avatar' />
|
||||
</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
0
src/components/Navigation/Navigation.scss
Normal file
0
src/components/Navigation/Navigation.scss
Normal file
@ -19,6 +19,7 @@ import {getRequestDates, setReportDate, setRequestDate} from "../../redux/report
|
||||
|
||||
import 'moment/locale/ru'
|
||||
import './profileCalendar.scss'
|
||||
import { Navigation } from '../Navigation/Navigation';
|
||||
|
||||
|
||||
export const ProfileCalendar = () => {
|
||||
@ -65,6 +66,7 @@ export const ProfileCalendar = () => {
|
||||
return (
|
||||
<div className='profile__calendar'>
|
||||
<ProfileHeader/>
|
||||
<Navigation />
|
||||
<div className='container'>
|
||||
<ProfileBreadcrumbs links={[{name: 'Главная', link: '/profile'},{name: 'Ваша отчетность', link: '/profile/calendar'}]} />
|
||||
<h2 className='summary__title'>Ваши отчеты</h2>
|
||||
|
@ -4,76 +4,19 @@ import {useDispatch, useSelector} from "react-redux";
|
||||
|
||||
import {Loader} from '../Loader/Loader'
|
||||
import {apiRequest} from "../../api/request";
|
||||
import {auth, getProfileInfo, setProfileInfo} from "../../redux/outstaffingSlice";
|
||||
import {auth, setProfileInfo} from "../../redux/outstaffingSlice";
|
||||
import {getRole} from "../../redux/roleSlice";
|
||||
|
||||
import {urlForLocal} from "../../helper";
|
||||
|
||||
import avatarMok from "../../pages/PartnerTreaties/Images/avatarMok.png"
|
||||
|
||||
import './profileHeader.scss'
|
||||
|
||||
|
||||
|
||||
|
||||
export const ProfileHeader = () => {
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
|
||||
const userRole = useSelector(getRole);
|
||||
const profileInfo = useSelector(getProfileInfo);
|
||||
const [user] = useState(localStorage.getItem('role_status') === '18' ? 'partner' : 'developer')
|
||||
const [navInfo] = useState({
|
||||
developer: [
|
||||
{
|
||||
path: '/summary',
|
||||
name: 'Резюме'
|
||||
},
|
||||
{
|
||||
path: '/calendar',
|
||||
name: 'Отчетность'
|
||||
},
|
||||
{
|
||||
path: '/tracker',
|
||||
name: 'Трекер'
|
||||
},
|
||||
{
|
||||
path: '/payouts',
|
||||
name: 'Выплаты'
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
name: 'Настройки'
|
||||
},
|
||||
],
|
||||
partner: [
|
||||
{
|
||||
path: '/catalog',
|
||||
name: 'Каталог'
|
||||
},
|
||||
{
|
||||
path: '/requests',
|
||||
name: 'Запросы'
|
||||
},
|
||||
{
|
||||
path: '/categories',
|
||||
name: 'Персонал'
|
||||
},
|
||||
{
|
||||
path: '/tracker',
|
||||
name: 'Трекер'
|
||||
},
|
||||
{
|
||||
path: '/treaties',
|
||||
name: 'Договора'
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
name: 'Настройки'
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
||||
|
||||
@ -113,29 +56,6 @@ export const ProfileHeader = () => {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='profileHeader__info'>
|
||||
<div className='profileHeader__container'>
|
||||
<nav className='profileHeader__nav'>
|
||||
{
|
||||
navInfo[user].map((link, index) => {
|
||||
return <NavLink key={index} end to={`/profile${link.path}`}>{link.name}</NavLink>
|
||||
})
|
||||
}
|
||||
</nav>
|
||||
|
||||
<div className='profileHeader__personalInfo'>
|
||||
<h3 className='profileHeader__personalInfoName'>
|
||||
{user === 'developer' ?
|
||||
profileInfo?.fio :
|
||||
''
|
||||
}
|
||||
</h3>
|
||||
<NavLink end to={'/profile'}>
|
||||
<img src={profileInfo.photo ? urlForLocal(profileInfo.photo) : avatarMok} className='profileHeader__personalInfoAvatar' alt='avatar'/>
|
||||
</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
};
|
||||
|
@ -22,6 +22,7 @@ import arrow from "../../images/right-arrow.png";
|
||||
|
||||
import './reportForm.scss'
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import { Navigation } from '../Navigation/Navigation'
|
||||
|
||||
const ReportForm = () => {
|
||||
if(localStorage.getItem('role_status') === '18') {
|
||||
@ -99,6 +100,7 @@ const ReportForm = () => {
|
||||
return (
|
||||
<section className='report-form'>
|
||||
<ProfileHeader/>
|
||||
<Navigation />
|
||||
<div className='container'>
|
||||
<ProfileBreadcrumbs links={[{name: 'Главная', link: '/profile'},
|
||||
{name: 'Ваша отчетность', link: '/profile/calendar'},
|
||||
|
44
src/components/StarRating/StarRating.js
Normal file
44
src/components/StarRating/StarRating.js
Normal file
@ -0,0 +1,44 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import './StarRating.scss'
|
||||
|
||||
const StarRating = ({ countStars=1, countActiveStars=1, color='#52B709', size=61 }) => {
|
||||
const [shadedStars, setShadedStars] = useState([])
|
||||
const [noShadedStars, setNoShadedStars] = useState([])
|
||||
const percent = (Math.abs(countActiveStars)) >= countStars ? 100 : (countActiveStars * 100 / countStars)
|
||||
useEffect(() => {
|
||||
for (let index = 0; index < countStars; index++) {
|
||||
setShadedStars(prev => [...prev, '★'])
|
||||
setNoShadedStars(prev => [...prev, '☆'])
|
||||
}
|
||||
}, [])
|
||||
|
||||
const ratingStyle = {
|
||||
"--size": size+'px'
|
||||
}
|
||||
const activeStyle = {
|
||||
"--width": percent + '%',
|
||||
"--color": color,
|
||||
"--content": shadedStars.join('')
|
||||
}
|
||||
const bodyStyle = {
|
||||
"--content": noShadedStars.join(''),
|
||||
"--color": color
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='rating' style={ratingStyle}>
|
||||
<div className="rating__body" style={bodyStyle} data-content={noShadedStars.join('')}>
|
||||
<div className="rating__active" style={activeStyle} data-content={shadedStars.join('')}></div>
|
||||
<div className="rating__items">
|
||||
<input type='radio' className="rating__item" value={1} name='star'></input>
|
||||
<input type='radio' className="rating__item" value={2} name='star'></input>
|
||||
<input type='radio' className="rating__item" value={3} name='star'></input>
|
||||
<input type='radio' className="rating__item" value={4} name='star'></input>
|
||||
<input type='radio' className="rating__item" value={5} name='star'></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(StarRating);
|
45
src/components/StarRating/StarRating.scss
Normal file
45
src/components/StarRating/StarRating.scss
Normal file
@ -0,0 +1,45 @@
|
||||
.rating{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: var(--size);
|
||||
line-height: 0.75;
|
||||
&__body{
|
||||
position: relative;
|
||||
&::before{
|
||||
content: attr(data-content);
|
||||
display: block;
|
||||
color: var(--color);
|
||||
}
|
||||
}
|
||||
&__active{
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: var(--width);
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
&::before{
|
||||
content: attr(data-content);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: var(--color);
|
||||
}
|
||||
}
|
||||
&__items{
|
||||
display: flex;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
&__item{
|
||||
flex: 0 0 20%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
14
src/components/features/Candidate-lk/HeadBottom.js
Normal file
14
src/components/features/Candidate-lk/HeadBottom.js
Normal file
@ -0,0 +1,14 @@
|
||||
import React from 'react'
|
||||
import medium_male from "../../../images/medium_male.png"
|
||||
|
||||
export const HeadBottom = () => {
|
||||
return (
|
||||
<div className="bottom-head">
|
||||
<div className="bottom-head__container">
|
||||
<div className="bottom-head__title">Мои тесты</div>
|
||||
<div className="bottom-head__img"><img src={medium_male} alt="" /></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
21
src/components/features/quiz/AlertResult.js
Normal file
21
src/components/features/quiz/AlertResult.js
Normal file
@ -0,0 +1,21 @@
|
||||
import React from 'react'
|
||||
import suucessIcon from '../../../images/quiz/success.png'
|
||||
|
||||
export const AlertResult = () => {
|
||||
|
||||
const successTest = false
|
||||
|
||||
return (
|
||||
<div className='alert-result'>
|
||||
<div className="alert-result__column">
|
||||
<img src={suucessIcon} alt="suucessIcon" className="alert-result__icon" />
|
||||
<div className='alert-result__text' style={{color: successTest ? '#52B709' : '#5B6871'}}>
|
||||
Благодарим Вас за прохождение теста "Junior разработчик". Ваши результаты проверены, готовы пригласить Вас в команду
|
||||
</div>
|
||||
</div>
|
||||
{!successTest && <div className="alert-result__column">
|
||||
<button className="alert-result__button quiz-btn">Запросить еще попытку</button>
|
||||
</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
24
src/components/features/quiz/BlockCompletedTest.js
Normal file
24
src/components/features/quiz/BlockCompletedTest.js
Normal file
@ -0,0 +1,24 @@
|
||||
import React from 'react'
|
||||
import compltedImage from "../../../images/quiz/compltedImage.png"
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
export const BlockCompletedTest = () => {
|
||||
const id = localStorage.getItem('id')
|
||||
return (
|
||||
<div className='block-completed-test'>
|
||||
<div className="block-completed-test__container">
|
||||
<div className="block-completed-test__img">
|
||||
<img src={compltedImage} alt="" />
|
||||
</div>
|
||||
<div className="block-completed-test__title">
|
||||
Спасибо! <br />
|
||||
Ваши ответы получены
|
||||
</div>
|
||||
<div className="block-completed-test__text">
|
||||
В течении дня в вашем кабинете будет отображены данные о прохождении тестирования
|
||||
</div>
|
||||
<Link to={'/profile-candidate/' + id} className="block-completed-test__button quiz-btn">В кабинет</Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
19
src/components/features/quiz/Card-introduction.js
Normal file
19
src/components/features/quiz/Card-introduction.js
Normal file
@ -0,0 +1,19 @@
|
||||
import React from 'react'
|
||||
import questionIcon from "../../../images/question.png"
|
||||
import './quiz.scss'
|
||||
|
||||
export const CardIntroduction = ({ title, description }) => {
|
||||
return (
|
||||
<div className="card-introduction">
|
||||
<div className="card-introduction__title">{title}</div>
|
||||
<div className="card-introduction__body">
|
||||
<div className="card-introduction__icon">
|
||||
<img src={questionIcon} alt="" />
|
||||
</div>
|
||||
<div className="card-introduction__text">
|
||||
{description}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
34
src/components/features/quiz/CardAviableTest.js
Normal file
34
src/components/features/quiz/CardAviableTest.js
Normal file
@ -0,0 +1,34 @@
|
||||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import rightArrow from "../../../images/arrowRight.png"
|
||||
import StarRating from '../../StarRating/StarRating'
|
||||
|
||||
|
||||
export const CardAvailableTest = ({ title, description, path, passedTest }) => {
|
||||
|
||||
|
||||
return (
|
||||
<div className='card-available-test'>
|
||||
<Link to={`/${path}`} className="card-available-test__container" style={{ opacity: passedTest ? 0.3 : 1, pointerEvents: passedTest ? 'none' : 'all' }}>
|
||||
<div className='card-available-test__top-head'>
|
||||
<StarRating />
|
||||
<h3 className='card-available-test__title'>{title}</h3>
|
||||
</div>
|
||||
<div className='card-available-test__info'>
|
||||
<p dangerouslySetInnerHTML={{ __html: description }}></p>
|
||||
<div className='card-available-test__infoLink'>
|
||||
<img src={rightArrow} alt='arrow' />
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{passedTest && <div className='card-available-test__finished'>
|
||||
<p>
|
||||
Получить отчет по тестированию
|
||||
</p>
|
||||
<Link to={'/quiz/report'}>Отчет по тесту</Link>
|
||||
</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -1,38 +1,38 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { selectedAnswersSelector, selectedTest } from '../../../redux/quizSlice';
|
||||
|
||||
export const GetOptionTask = ({ type, answer, handleChange, inputValue}) => {
|
||||
|
||||
const id = localStorage.getItem('id');
|
||||
const dataTest = useSelector(selectedTest);
|
||||
|
||||
|
||||
export const GetOptionTask = ({type, answer, handleChange, inputValue}) => {
|
||||
switch (type) {
|
||||
case "3":
|
||||
return (
|
||||
<div className="form-task__group" key={answer.id}>
|
||||
<input className='form-task__check' type="checkbox" value={answer.answer_body} id={answer.id}
|
||||
onChange={handleChange}/>
|
||||
<label htmlFor={answer.id}>{answer.answer_body}</label>
|
||||
</div>
|
||||
)
|
||||
case "2":
|
||||
case "4":
|
||||
return (
|
||||
<div className="form-task__group" key={answer.id}>
|
||||
<input className='form-task__check' type="radio" value={answer.answer_body} name={'radio'} id={answer.id}
|
||||
onChange={handleChange}/>
|
||||
<label htmlFor={answer.id}>{answer.answer_body}</label>
|
||||
</div>
|
||||
)
|
||||
case "1":
|
||||
return (
|
||||
<div className="form-task__group">
|
||||
<textarea className='form-task__field' value={inputValue}
|
||||
onChange={handleChange}/>
|
||||
</div>
|
||||
<div className="form-task__group">
|
||||
<textarea className='form-task__field' value={inputValue}
|
||||
onChange={handleChange} />
|
||||
</div>
|
||||
)
|
||||
case "3":
|
||||
return (
|
||||
<div className="form-task__group" key={answer.id}>
|
||||
<input className='form-task__check' type="checkbox"
|
||||
value={answer.answer_body} id={answer.id}
|
||||
onChange={handleChange} />
|
||||
<label htmlFor={answer.id}>{answer.answer_body}</label>
|
||||
</div>
|
||||
)
|
||||
|
||||
default:
|
||||
return (
|
||||
<div className="form-task__group" key={answer.id}>
|
||||
<input className='form-task__check' type="checkbox" value={answer.answer_body} id={answer.id}
|
||||
onChange={handleChange}/>
|
||||
<label htmlFor={answer.id}>{answer.answer_body}</label>
|
||||
</div>
|
||||
<div className="form-task__group" key={answer.id}>
|
||||
<input className='form-task__check' type="radio" value={answer.answer_body} name={'radio'} id={answer.id}
|
||||
onChange={handleChange} />
|
||||
<label htmlFor={answer.id}>{answer.answer_body}</label>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ export const HeaderQuiz = ({header}) => {
|
||||
}, [userId, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
apiRequest(`/user-questionnaire/questionnaires-list?user_id=${userId}`)
|
||||
.then(res => dispatch(setQuestionnairesList(res)))
|
||||
// apiRequest(`/user-questionnaire/questionnaires-list?user_id=${userId}`)
|
||||
// .then(res => dispatch(setQuestionnairesList(res)))
|
||||
}, [userId, dispatch]);
|
||||
|
||||
return (
|
||||
|
80
src/components/features/quiz/Quiz-passing-information.js
Normal file
80
src/components/features/quiz/Quiz-passing-information.js
Normal file
@ -0,0 +1,80 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import timer from '../../../images/quiz/timer.png'
|
||||
import accempt from '../../../images/quiz/accempt.png'
|
||||
import { useTimer } from 'react-timer-hook';
|
||||
import StarRating from '../../StarRating/StarRating';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { completedTestSelector } from '../../../redux/quizSlice';
|
||||
|
||||
|
||||
export const QuizPassingInformation = ({ expiryTimestamp, setStartTest }) => {
|
||||
const {
|
||||
seconds,
|
||||
minutes,
|
||||
isRunning,
|
||||
start,
|
||||
pause,
|
||||
resume,
|
||||
restart
|
||||
} = useTimer({
|
||||
expiryTimestamp, autoStart: false, onExpire: () => {
|
||||
console.warn('onExpire called')
|
||||
}
|
||||
});
|
||||
const completedTest = useSelector(completedTestSelector)
|
||||
|
||||
|
||||
const startTesting = () => {
|
||||
setStartTest(true)
|
||||
start()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (completedTest) {
|
||||
const time = new Date();
|
||||
time.setSeconds(time.getSeconds() + 0);//600 - кол-во секунд для прохождения теста
|
||||
restart(time, false)
|
||||
}
|
||||
}, [completedTest])
|
||||
|
||||
|
||||
return (
|
||||
<div className='quiz-passing-information'>
|
||||
<div className="quiz-passing-information__container">
|
||||
<div className="quiz-passing-information__main">
|
||||
<div className="quiz-passing-information__specialization">
|
||||
<StarRating color={'#52B709'} countStars={1} countActiveStars={0.5} size={61} />
|
||||
<div className='quiz-passing-information__specialization-title'>
|
||||
Junior <br />
|
||||
разработчик
|
||||
</div>
|
||||
</div>
|
||||
<div className="quiz-passing-information__timer timer-quiz">
|
||||
<div className="quiz-passing-information__icon">
|
||||
<img src={timer} alt="" />
|
||||
</div>
|
||||
<div className="quiz-passing-information__text">
|
||||
{completedTest ? 'Время вышло' : 'Время на прохождение теста:'} <br />
|
||||
<span>{minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0')} секунд</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="quiz-passing-information__attempt">
|
||||
<div className="quiz-passing-information__icon">
|
||||
<img src={accempt} alt="" />
|
||||
</div>
|
||||
<div className="quiz-passing-information__text">
|
||||
Попыток прохождения: <br />
|
||||
<span>1 попытка</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{(!completedTest && !isRunning) && <button className="quiz-passing-information__button btn-green" onClick={startTesting}>Начать</button>}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* {isRunning && <button className="quiz-passing-information__button quiz-btn" onClick={pause}>Завершить</button>} */}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
27
src/components/features/quiz/QuizReport.js
Normal file
27
src/components/features/quiz/QuizReport.js
Normal file
@ -0,0 +1,27 @@
|
||||
import React from 'react'
|
||||
import StarRating from '../../StarRating/StarRating'
|
||||
|
||||
export const QuizReport = () => {
|
||||
return (
|
||||
<div className='report'>
|
||||
<div className="report__row">
|
||||
<div className="report__column">
|
||||
<StarRating color={'#52B709'} countStars={1} countActiveStars={0.5} size={61} />
|
||||
<div className="report__job-title">Junior <br /> разработчик</div>
|
||||
</div>
|
||||
<div className="report__column">
|
||||
<div className="report__value">22</div>
|
||||
<div className="report__text">Правильных ответов</div>
|
||||
</div>
|
||||
<div className="report__column">
|
||||
<div className="report__value report__value_false">02</div>
|
||||
<div className="report__text">Не правильных ответов</div>
|
||||
</div>
|
||||
<div className="report__column">
|
||||
<div className="report__status-text">Статус:</div>
|
||||
<div className="report__status">Пройдено!</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
20
src/components/features/quiz/SelectedCategory.js
Normal file
20
src/components/features/quiz/SelectedCategory.js
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react'
|
||||
import iconSpecialization from "../../../images/icon-specialization.png"
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
export const SelectedCategory = ({setSelectedCategory}) => {
|
||||
return (
|
||||
<div className='selected-category'>
|
||||
<div className="selected-category__container">
|
||||
<div className="selected-category__title">Ваша выбранная <br /> категория</div>
|
||||
<div className="selected-category__category">
|
||||
<div className="selected-category__image">
|
||||
<img src={iconSpecialization} alt="" />
|
||||
</div>
|
||||
<div className="selected-category__title-category">Backend <br /> разработчики</div>
|
||||
</div>
|
||||
<button onClick={()=>setSelectedCategory(true)} className="selected-category__button">Заменить специализацию </button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,39 +1,42 @@
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {useNavigate} from "react-router-dom"
|
||||
import {useSelector, useDispatch} from 'react-redux'
|
||||
|
||||
import {Progressbar} from './ProgressbarQuiz'
|
||||
import {GetOptionTask} from './GetOptionTask'
|
||||
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import questionIcon from "../../../images/question.png"
|
||||
import { Progressbar } from './ProgressbarQuiz'
|
||||
import { GetOptionTask } from './GetOptionTask'
|
||||
import {
|
||||
fetchUserAnswersMany, fetchUserAnswerOne, fetchGetAnswers, selectAnswer, selectedTest
|
||||
fetchUserAnswersMany, fetchUserAnswerOne, fetchGetAnswers, answersSelector, selectedTest, questionsSelector, setAnswers, setCompleteTest
|
||||
} from './../../../redux/quizSlice'
|
||||
|
||||
import './quiz.scss'
|
||||
import {apiRequest} from "../../../api/request";
|
||||
import { apiRequest } from "../../../api/request";
|
||||
import { HeaderQuiz } from './HeaderQuiz'
|
||||
|
||||
export const TaskQuiz = () => {
|
||||
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const listAnswers = useSelector(selectAnswer);
|
||||
const dataTest = useSelector(selectedTest);
|
||||
const answers = useSelector(answersSelector);
|
||||
const questions = useSelector(questionsSelector);
|
||||
|
||||
|
||||
const dataTest = useSelector(selectedTest);
|
||||
const [index, setIndex] = useState(0);
|
||||
const [checkedValues, setCheckedValues] = useState([]);
|
||||
const [stripValue, setStripValue] = useState(0);
|
||||
//const [stripValue, setStripValue] = useState(0);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [questions, setQuestions] = useState([]);
|
||||
|
||||
|
||||
|
||||
const id = localStorage.getItem('id');
|
||||
|
||||
useEffect(() => {
|
||||
// fetch('https://itguild.info/api/user-questionnaire/questionnaires-list?user_id=110').then(response => response.json())
|
||||
// .then(json => console.log(json))
|
||||
apiRequest(`/question/get-questions?uuid=${dataTest.uuid}`)
|
||||
.then((response) => {
|
||||
console.log(response)
|
||||
setQuestions(response);
|
||||
dispatch(fetchGetAnswers(response[0].id));
|
||||
setStripValue((+index + 1) * 100 / response.length)
|
||||
})
|
||||
@ -41,108 +44,98 @@ export const TaskQuiz = () => {
|
||||
|
||||
const nextQuestion = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
//Проверка на валидацию ответов
|
||||
if (checkedValues.length || inputValue) {
|
||||
switch (questions[index].question_type_id) {
|
||||
case '3':
|
||||
dispatch(fetchUserAnswersMany(checkedValues));
|
||||
break;
|
||||
case '2':
|
||||
case '1':
|
||||
case '4':
|
||||
dispatch(fetchUserAnswerOne(checkedValues));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//Проверка на существование следующего запроса
|
||||
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)));
|
||||
setCheckedValues([]);
|
||||
setInputValue('')
|
||||
} else {
|
||||
navigate(`/quiz/result`);
|
||||
alert("Тест пройден!")
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!(checkedValues.length || inputValue)) {
|
||||
alert("Вы не ответили на вопрос")
|
||||
return
|
||||
}
|
||||
|
||||
//отправка ответов на сервер
|
||||
if (questions[index].question_type_id != 3) {
|
||||
//dispatch(fetchUserAnswerOne(checkedValues));
|
||||
} else {
|
||||
console.log(checkedValues);
|
||||
// dispatch(fetchUserAnswersMany(checkedValues));
|
||||
}
|
||||
|
||||
//Проверка на окончание теста
|
||||
if (!(index < questions.length - 1)) {
|
||||
dispatch(setCompleteTest())
|
||||
return
|
||||
}
|
||||
|
||||
dispatch(fetchGetAnswers(questions[index + 1].id));
|
||||
setIndex(prev => prev + 1);
|
||||
setCheckedValues([]);
|
||||
setInputValue('')
|
||||
};
|
||||
|
||||
|
||||
const handleChange = (e) => {
|
||||
const checked = e.target.checked;
|
||||
switch (questions[index].question_type_id) {
|
||||
case '3':
|
||||
checked ? setCheckedValues(prev => [...prev, {
|
||||
user_id: id,
|
||||
user_questionnaire_uuid: dataTest.uuid,
|
||||
question_id: questions[index].id,
|
||||
response_body: e.target.value
|
||||
}]) :
|
||||
setCheckedValues(prev => [...prev.filter(item => item.response_body !== e.target.value)]);
|
||||
break;
|
||||
case '1':
|
||||
case '2':
|
||||
case '4':
|
||||
setCheckedValues([{
|
||||
user_id: id,
|
||||
user_questionnaire_uuid: dataTest.uuid,
|
||||
question_id: questions[index].id,
|
||||
response_body: e.target.value
|
||||
}])
|
||||
|
||||
if (questions[index].question_type_id != 3) {
|
||||
setCheckedValues([{
|
||||
user_id: id,
|
||||
user_questionnaire_uuid: dataTest.uuid,
|
||||
question_id: questions[index].id,
|
||||
response_body: e.target.value
|
||||
}])
|
||||
return
|
||||
}
|
||||
|
||||
checked ? setCheckedValues(prev => [...prev, {
|
||||
user_id: id,
|
||||
user_questionnaire_uuid: dataTest.uuid,
|
||||
question_id: questions[index].id,
|
||||
response_body: e.target.value
|
||||
}]) :
|
||||
setCheckedValues(prev => [...prev.filter(item => item.response_body !== e.target.value)]);
|
||||
};
|
||||
|
||||
|
||||
console.log('render task');
|
||||
|
||||
|
||||
return (
|
||||
<React.StrictMode>
|
||||
<Progressbar indexQuestion={index + 1} width={stripValue}/>
|
||||
<div className="task">
|
||||
{!questions.length || !stripValue || !listAnswers.length ?
|
||||
<h1 className={'_container'} style={{display: "block"}}>Loading....</h1>
|
||||
:
|
||||
<div className="task__container">
|
||||
<div className="task__code code">
|
||||
{/* <CodeSnippetlighter /> */}
|
||||
</div>
|
||||
<h3 className="task__title quiz-title_h3">{questions[index].question_body}</h3>
|
||||
<div className="task__body">
|
||||
<form className='task__form form-task'>
|
||||
{
|
||||
questions[index].question_type_id === 1 ?
|
||||
<GetOptionTask
|
||||
type={1}
|
||||
inputValue={checkedValues.length ? checkedValues[0].response_body : ''}
|
||||
handleChange={handleChange}
|
||||
/>
|
||||
:
|
||||
listAnswers.map((answer) => (
|
||||
<GetOptionTask
|
||||
key={answer.id}
|
||||
type={questions[index].question_type_id}
|
||||
handleChange={handleChange}
|
||||
answer={answer}
|
||||
/>
|
||||
))
|
||||
}
|
||||
<div className="form-task__buttons">
|
||||
{questions.length !== index + 1 &&
|
||||
<button type='submit' className='quiz-btn'
|
||||
onClick={(e) => nextQuestion(e)}>Далее</button>}
|
||||
{questions.length === index + 1 &&
|
||||
<button onClick={(e) => nextQuestion(e)}
|
||||
className='quiz-btn quiz-btn_dark-green'>Завершить</button>}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div className="task">
|
||||
{
|
||||
<div className="task__container">
|
||||
<div className='task__header'>
|
||||
<img src={questionIcon} alt="" />
|
||||
<h3 className="task__title quiz-title_h3">{questions[index].question_body}</h3>
|
||||
</div>
|
||||
|
||||
<div className="task__body">
|
||||
<form className='task__form form-task' onSubmit={nextQuestion}>
|
||||
{
|
||||
|
||||
answers.map((answer) => (
|
||||
<GetOptionTask
|
||||
key={answer.id}
|
||||
type={questions[index].question_type_id}
|
||||
handleChange={handleChange}
|
||||
answer={answer}
|
||||
/>
|
||||
))
|
||||
}
|
||||
<div className="form-task__buttons">
|
||||
{/* {
|
||||
index != 0 && <button type='submit' className='form-task__btn quiz-btn quiz-btn_back'
|
||||
onClick={prevQuestion}>Назад</button>
|
||||
} */}
|
||||
{
|
||||
index != questions.length && <button onClick={nextQuestion}
|
||||
className='form-task__btn quiz-btn'>Далее</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</React.StrictMode>
|
||||
|
||||
}
|
||||
</div>
|
||||
|
||||
)
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
BIN
src/images/icon-specialization.png
Normal file
BIN
src/images/icon-specialization.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 2.2 KiB |
BIN
src/images/note.png
Normal file
BIN
src/images/note.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 547 B |
BIN
src/images/question.png
Normal file
BIN
src/images/question.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 803 B |
BIN
src/images/quiz/accempt.png
Normal file
BIN
src/images/quiz/accempt.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 1.2 KiB |
BIN
src/images/quiz/compltedImage.png
Normal file
BIN
src/images/quiz/compltedImage.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 2.8 KiB |
BIN
src/images/quiz/success.png
Normal file
BIN
src/images/quiz/success.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 2.2 KiB |
BIN
src/images/quiz/timer.png
Normal file
BIN
src/images/quiz/timer.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 1.1 KiB |
@ -19,7 +19,7 @@ code {
|
||||
}
|
||||
|
||||
h1 {
|
||||
display: none;
|
||||
|
||||
}
|
||||
|
||||
.container {
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
.auth {
|
||||
&__wrapper {
|
||||
background: #FFFFFF;
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
padding: 50px 0 35px 56px;
|
||||
margin-top: 40px;
|
||||
@ -55,7 +55,7 @@
|
||||
|
||||
input {
|
||||
margin-bottom: 30px;
|
||||
background: #EFF2F7;
|
||||
background: #eff2f7;
|
||||
border-radius: 8px;
|
||||
min-width: 300px;
|
||||
width: 100%;
|
||||
@ -80,7 +80,7 @@
|
||||
}
|
||||
|
||||
button {
|
||||
background: #52B709;
|
||||
background: #52b709;
|
||||
border-radius: 44px;
|
||||
max-width: 130px;
|
||||
border: none;
|
||||
@ -107,7 +107,7 @@
|
||||
text-align: center;
|
||||
|
||||
span {
|
||||
color: #52B709;
|
||||
color: #52b709;
|
||||
}
|
||||
|
||||
@media (max-width: 660px) {
|
||||
@ -122,13 +122,19 @@
|
||||
}
|
||||
|
||||
&__categoriesWrapper {
|
||||
display: flex;
|
||||
position: relative;
|
||||
margin-top: 200px;
|
||||
flex-wrap: wrap;
|
||||
row-gap: 24px;
|
||||
column-gap: 21px;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-gap: 24px;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
margin-bottom: 24px;
|
||||
@media (max-width: 955px) {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
@media (max-width: 668px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.categoriesItem {
|
||||
@media (max-width: 1094px) {
|
||||
|
@ -11,6 +11,7 @@ import {ProfileHeader} from "../../components/ProfileHeader/ProfileHeader";
|
||||
import {ProfileBreadcrumbs} from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs"
|
||||
import {apiRequest} from "../../api/request";
|
||||
import {Navigate} from "react-router-dom";
|
||||
import { Navigation } from '../../components/Navigation/Navigation'
|
||||
|
||||
|
||||
const Home = () => {
|
||||
@ -58,6 +59,7 @@ const Home = () => {
|
||||
return (
|
||||
<>
|
||||
<ProfileHeader/>
|
||||
<Navigation />
|
||||
<div className="catalog">
|
||||
<div className='container'>
|
||||
<ProfileBreadcrumbs links={[
|
||||
|
@ -15,6 +15,7 @@ import deleteIcon from "../../images/close.png"
|
||||
|
||||
|
||||
import './partnerAddRequest.scss'
|
||||
import { Navigation } from '../../components/Navigation/Navigation';
|
||||
|
||||
export const PartnerAddRequest = () => {
|
||||
if(localStorage.getItem('role_status') !== '18') {
|
||||
@ -81,6 +82,7 @@ export const PartnerAddRequest = () => {
|
||||
return (
|
||||
<div className='partnerAddRequest'>
|
||||
<ProfileHeader />
|
||||
<Navigation />
|
||||
<div className='container'>
|
||||
<ProfileBreadcrumbs links={[
|
||||
{name: 'Главная', link: '/profile'},
|
||||
|
@ -1,20 +1,22 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Link, Navigate} from "react-router-dom";
|
||||
import {Link, Navigate, useNavigate} from "react-router-dom";
|
||||
import {useSelector, useDispatch} from 'react-redux'
|
||||
import {getPartnerRequestId, getPartnerRequests, setPartnerRequestId} from '../../redux/outstaffingSlice'
|
||||
|
||||
import {ProfileHeader} from "../../components/ProfileHeader/ProfileHeader";
|
||||
import {ProfileBreadcrumbs} from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs"
|
||||
import {Footer} from "../../components/Footer/Footer";
|
||||
import { Navigation } from '../../components/Navigation/Navigation';
|
||||
import {Loader} from "../../components/Loader/Loader"
|
||||
|
||||
import {apiRequest} from "../../api/request";
|
||||
import {getCorrectDate} from "../../components/Calendar/calendarHelper";
|
||||
|
||||
import {urlForLocal} from '../../helper'
|
||||
|
||||
import arrowSwitchDate from "../../images/arrowViewReport.png";
|
||||
import backEndImg from "../../images/QualificationInfo.png";
|
||||
import middle from "../../images/QualificationInfoMiddle.png";
|
||||
import personImg from "../../images/mokPerson.png"
|
||||
import deleteBtn from "../../images/deleteBtn.png"
|
||||
|
||||
import './partnerBid.scss'
|
||||
@ -23,9 +25,11 @@ export const PartnerBid = () => {
|
||||
if(localStorage.getItem('role_status') !== '18') {
|
||||
return <Navigate to="/profile" replace/>
|
||||
}
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const requestId = useSelector(getPartnerRequestId);
|
||||
const partnerRequests = useSelector(getPartnerRequests);
|
||||
const navigate= useNavigate();
|
||||
|
||||
if (!requestId) {
|
||||
return <Navigate to="/profile/requests" replace/>
|
||||
@ -39,29 +43,37 @@ export const PartnerBid = () => {
|
||||
})
|
||||
}, [requestId]);
|
||||
|
||||
const deleteRequest = () => {
|
||||
apiRequest('/request/update-request', {
|
||||
method: 'PUT',
|
||||
data: {
|
||||
user_id: localStorage.getItem('id'),
|
||||
request_id: requestId,
|
||||
status: 0,
|
||||
// title: 'bro',
|
||||
// position_id: 1,
|
||||
// knowledge_level_id: 2,
|
||||
// specialist_count: 2,
|
||||
// descr: 'broooooo',
|
||||
// skill_ids: [1, 2, 3]
|
||||
}
|
||||
}).then((res) => {
|
||||
navigate('/profile/requests');
|
||||
})
|
||||
};
|
||||
|
||||
const [requestInfo, setRequestInfo] = useState({})
|
||||
const [loader, setLoader] = useState(false)
|
||||
|
||||
const [mokPersons] = useState([
|
||||
{
|
||||
name: 'Дмитрий, PHP Back end - разработчик, Middle',
|
||||
link: '/candidate/110',
|
||||
img: personImg
|
||||
},
|
||||
{
|
||||
name: 'Дмитрий, PHP Back end - разработчик, Middle',
|
||||
link: '/candidate/111',
|
||||
img: personImg
|
||||
},
|
||||
{
|
||||
name: 'Дмитрий, PHP Back end - разработчик, Middle',
|
||||
link: '/candidate/112',
|
||||
img: personImg
|
||||
}
|
||||
])
|
||||
const [levels] = useState({
|
||||
1: "Junior",
|
||||
2: "Middle",
|
||||
3: "Middle+",
|
||||
4: "Senior",
|
||||
})
|
||||
return (
|
||||
<div className='partnerBid'>
|
||||
<ProfileHeader />
|
||||
<Navigation />
|
||||
<div className='container'>
|
||||
<ProfileBreadcrumbs links={[
|
||||
{name: 'Главная', link: '/profile'},
|
||||
@ -77,7 +89,7 @@ export const PartnerBid = () => {
|
||||
<h3>{requestInfo.title}</h3>
|
||||
<div className='partnerBid__qualification__buttons'>
|
||||
<button>Редактировать</button>
|
||||
<img src={deleteBtn} alt='delete'/>
|
||||
<img src={deleteBtn} alt='delete' onClick={() => deleteRequest()}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='partnerBid__switcher'>
|
||||
@ -100,6 +112,7 @@ export const PartnerBid = () => {
|
||||
</>
|
||||
}
|
||||
{Boolean(Object.keys(requestInfo).length) && !loader &&
|
||||
<>
|
||||
<div className='table__wrapper'>
|
||||
<table>
|
||||
<thead>
|
||||
@ -148,27 +161,25 @@ export const PartnerBid = () => {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
<div className='partnerBid__suitable'>
|
||||
<div className='partnerBid__suitable__title'>
|
||||
<p>Подходящие сотрудники по запросу</p>
|
||||
</div>
|
||||
<div className='partnerBid__suitable__persons'>
|
||||
{mokPersons.map((person, index) => {
|
||||
return <div key={index} className='partnerBid__suitable__person'>
|
||||
<img src={person.img} alt='avatar' />
|
||||
<p>{person.name}</p>
|
||||
<Link className='partnerBid__suitable__person__more' to={person.link}>
|
||||
Подробнее
|
||||
</Link>
|
||||
<div className='partnerBid__suitable__person__info'>
|
||||
|
||||
<div className='partnerBid__suitable'>
|
||||
<div className='partnerBid__suitable__title'>
|
||||
<p>Подходящие сотрудники по запросу</p>
|
||||
</div>
|
||||
<div className='partnerBid__suitable__persons'>
|
||||
{requestInfo.result_profiles.length && requestInfo.result_profiles.map((person, index) => {
|
||||
return <div key={index} className='partnerBid__suitable__person'>
|
||||
<img src={urlForLocal(person.photo)} alt='avatar' />
|
||||
<p>{person.fio} - {person.position_title}, {levels[person.level]}</p>
|
||||
<Link className='partnerBid__suitable__person__more' to={`/candidate/${person.id}`}>
|
||||
Подробнее
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
<Footer/>
|
||||
</div>
|
||||
|
@ -59,6 +59,7 @@
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
margin-left: 21px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -339,6 +340,30 @@
|
||||
row-gap: 18px;
|
||||
position: relative;
|
||||
top: -15px;
|
||||
max-height: 420px;
|
||||
overflow: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
background: 0 0;
|
||||
box-shadow: 0 0 14px rgba(0, 0, 0, 0.04);
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #8db274;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #c5c0c6;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__person {
|
||||
@ -353,8 +378,9 @@
|
||||
}
|
||||
|
||||
img {
|
||||
width: 88px;
|
||||
max-width: 88px;
|
||||
height: 88px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
p {
|
||||
@ -364,6 +390,7 @@
|
||||
line-height: 32px;
|
||||
margin-left: 60px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
|
@ -14,6 +14,7 @@ import {apiRequest} from "../../api/request";
|
||||
import cursorImg from "../../images/cursorImg.png"
|
||||
|
||||
import './partnerRequests.scss'
|
||||
import { Navigation } from '../../components/Navigation/Navigation';
|
||||
|
||||
export const PartnerRequests = () => {
|
||||
if(localStorage.getItem('role_status') !== '18') {
|
||||
@ -33,6 +34,7 @@ export const PartnerRequests = () => {
|
||||
return (
|
||||
<div className='partnerRequests'>
|
||||
<ProfileHeader />
|
||||
<Navigation />
|
||||
<div className='container'>
|
||||
<ProfileBreadcrumbs links={[
|
||||
{name: 'Главная', link: '/profile'},
|
||||
|
@ -7,11 +7,13 @@ import { ProfileHeader } from "../../components/ProfileHeader/ProfileHeader";
|
||||
import kontur from "../../images/konturLogo.png";
|
||||
import astral from "../../images/astralLogo.png";
|
||||
import "./partnerSettings.scss";
|
||||
import { Navigation } from "../../components/Navigation/Navigation";
|
||||
|
||||
export const PartnerSettings = () => {
|
||||
return (
|
||||
<div className="settings">
|
||||
<ProfileHeader />
|
||||
<Navigation />
|
||||
<div className="container settings__page">
|
||||
<ProfileBreadcrumbs
|
||||
links={[
|
||||
|
@ -15,6 +15,7 @@ import lockDone from "./Images/lockDone.svg"
|
||||
import avatarMok from "./Images/avatarMok.png"
|
||||
|
||||
import './partnerTreaties.scss'
|
||||
import { Navigation } from '../../components/Navigation/Navigation';
|
||||
|
||||
export const PartnerTreaties = () => {
|
||||
const [toggleTab, setToggleTab] = useState(1);
|
||||
@ -25,6 +26,7 @@ export const PartnerTreaties = () => {
|
||||
return(
|
||||
<div className="treaties">
|
||||
<ProfileHeader/>
|
||||
<Navigation />
|
||||
<div className="container">
|
||||
<ProfileBreadcrumbs
|
||||
links={[
|
||||
|
@ -22,6 +22,7 @@ import rightArrow from "../../images/arrowRight.png"
|
||||
import avatarImg from "../PartnerEmployees/avatarMok.png";
|
||||
|
||||
import "./partnerСategories.scss"
|
||||
import { Navigation } from '../../components/Navigation/Navigation';
|
||||
|
||||
export const PartnerCategories = () => {
|
||||
const dispatch = useDispatch();
|
||||
@ -130,6 +131,7 @@ export const PartnerCategories = () => {
|
||||
return (
|
||||
<div className="partnerCategories">
|
||||
<ProfileHeader />
|
||||
<Navigation />
|
||||
<div className="container">
|
||||
<ProfileBreadcrumbs links={[
|
||||
{name: 'Главная', link: '/profile'},
|
||||
|
@ -1,11 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
import {ProfileHeader} from "../../components/ProfileHeader/ProfileHeader";
|
||||
import { Navigation } from '../../components/Navigation/Navigation';
|
||||
|
||||
export const Payouts = () => {
|
||||
return (
|
||||
<div className='payouts'>
|
||||
<ProfileHeader />
|
||||
<Navigation />
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
@ -5,6 +5,8 @@ import {Link} from "react-router-dom";
|
||||
import {ProfileHeader} from "../../components/ProfileHeader/ProfileHeader";
|
||||
import {ProfileBreadcrumbs} from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs"
|
||||
import {Footer} from "../../components/Footer/Footer";
|
||||
import { CardControl } from '../../components/CardControl/CardControl';
|
||||
import { Navigation } from '../../components/Navigation/Navigation';
|
||||
|
||||
import {getProfileInfo} from "../../redux/outstaffingSlice";
|
||||
|
||||
@ -17,8 +19,6 @@ import paymentIcon from "../../images/paymentIcon.png"
|
||||
import settingIcon from "../../images/settingIcon.png"
|
||||
import avatarMok from "../PartnerTreaties/Images/avatarMok.png"
|
||||
|
||||
import rightArrow from "../../images/arrowRight.png"
|
||||
|
||||
import './profile.scss'
|
||||
|
||||
export const Profile = () => {
|
||||
@ -28,31 +28,31 @@ export const Profile = () => {
|
||||
const [profileItemsInfo] = useState({
|
||||
developer: [
|
||||
{
|
||||
path: '/calendar',
|
||||
path: 'profile/calendar',
|
||||
img: reportsIcon,
|
||||
title: 'Ваша отчетность',
|
||||
description: '<span></span>Отработанных в этом месяце часов'
|
||||
},
|
||||
{
|
||||
path: '/summary',
|
||||
path: 'profile/summary',
|
||||
img: summaryIcon,
|
||||
title: 'Данные и резюме',
|
||||
description: 'Ваше резюме<br/><span>заполнено</span>'
|
||||
},
|
||||
{
|
||||
path: '/tracker',
|
||||
path: 'profile/tracker',
|
||||
img: timerIcon,
|
||||
title: 'Трекер времени',
|
||||
description: 'Сколько времени занимает<br/> выполнение задач'
|
||||
},
|
||||
{
|
||||
path: '/payouts',
|
||||
path: 'profile/payouts',
|
||||
img: paymentIcon,
|
||||
title: 'Выплаты',
|
||||
description: 'У вас <span>подтвержден</span><br/> статус самозанятого'
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
path: 'profile/settings',
|
||||
img: settingIcon,
|
||||
title: 'Настройки аккаунта',
|
||||
description: 'Перейдите чтобы начать<br/> редактирование'
|
||||
@ -60,31 +60,31 @@ export const Profile = () => {
|
||||
],
|
||||
partner: [
|
||||
{
|
||||
path: '/requests',
|
||||
path: 'profile/requests',
|
||||
img: reportsIcon,
|
||||
title: 'Запросы и открытые позиции',
|
||||
description: '<span>У вас 2 вакансии<br/></span>открытые от лица компании'
|
||||
},
|
||||
{
|
||||
path: '/categories',
|
||||
path: 'profile/categories',
|
||||
img: summaryIcon,
|
||||
title: 'Данные персонала',
|
||||
description: 'Наши специалисты <br/><span>уже работающие у вас</span>'
|
||||
},
|
||||
{
|
||||
path: '/tracker',
|
||||
path: 'profile/tracker',
|
||||
img: timerIcon,
|
||||
title: 'Трекер времени',
|
||||
description: 'Контроль времени и<br/> выполнение задач'
|
||||
},
|
||||
{
|
||||
path: '/treaties',
|
||||
path: 'profile/treaties',
|
||||
img: paymentIcon,
|
||||
title: 'Договора и отчетность',
|
||||
description: 'Ключевые условия<br/> договора'
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
path: 'profile/settings',
|
||||
img: settingIcon,
|
||||
title: 'Настройки аккаунта',
|
||||
description: 'Перейдите чтобы начать<br/> редактирование'
|
||||
@ -95,6 +95,7 @@ export const Profile = () => {
|
||||
return (
|
||||
<div className='profile'>
|
||||
<ProfileHeader/>
|
||||
<Navigation />
|
||||
<div className='container'>
|
||||
<ProfileBreadcrumbs links={[{name: 'Главная', link: '/profile'}]} />
|
||||
<h2 className='profile__title'>
|
||||
@ -119,18 +120,13 @@ export const Profile = () => {
|
||||
<div className='profile__items'>
|
||||
{
|
||||
profileItemsInfo[user].map((item, index) => {
|
||||
return <Link key={index} to={`/profile${item.path}`} className='item'>
|
||||
<div className='item__about'>
|
||||
<img src={item.img} alt='itemImg'/>
|
||||
<h3>{item.title}</h3>
|
||||
</div>
|
||||
<div className='item__info'>
|
||||
<p dangerouslySetInnerHTML={{__html: item.description}}></p>
|
||||
<div className='item__infoLink'>
|
||||
<img src={rightArrow} alt='arrow'/>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
return <CardControl
|
||||
description={item.description}
|
||||
img={item.img}
|
||||
path={item.path}
|
||||
title={item.title}
|
||||
key={index}
|
||||
/>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
@ -49,79 +49,79 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.item {
|
||||
max-width: 353px;
|
||||
width: 100%;
|
||||
padding: 35px 45px 15px 30px;
|
||||
background: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
// .item {
|
||||
// max-width: 353px;
|
||||
// width: 100%;
|
||||
// padding: 35px 45px 15px 30px;
|
||||
// background: #FFFFFF;
|
||||
// border-radius: 12px;
|
||||
// text-decoration: none;
|
||||
// cursor: pointer;
|
||||
// transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 6px 5px 20px rgb(87 98 80 / 21%);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
// &:hover {
|
||||
// box-shadow: 6px 5px 20px rgb(87 98 80 / 21%);
|
||||
// transform: scale(1.02);
|
||||
// }
|
||||
|
||||
@media (max-width: 1175px) {
|
||||
width: 48%;
|
||||
max-width: none;
|
||||
}
|
||||
// @media (max-width: 1175px) {
|
||||
// width: 48%;
|
||||
// max-width: none;
|
||||
// }
|
||||
|
||||
@media (max-width: 925px) {
|
||||
width: 100%;
|
||||
padding: 15px 25px;
|
||||
}
|
||||
// @media (max-width: 925px) {
|
||||
// width: 100%;
|
||||
// padding: 15px 25px;
|
||||
// }
|
||||
|
||||
&__about {
|
||||
display: flex;
|
||||
column-gap: 20px;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
// &__about {
|
||||
// display: flex;
|
||||
// column-gap: 20px;
|
||||
// align-items: center;
|
||||
// margin-bottom: 30px;
|
||||
|
||||
@media (max-width: 925px) {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
// @media (max-width: 925px) {
|
||||
// margin-bottom: 15px;
|
||||
// }
|
||||
|
||||
h3 {
|
||||
color: #000000;
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
max-width: 125px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
// h3 {
|
||||
// color: #000000;
|
||||
// font-weight: 500;
|
||||
// font-size: 18px;
|
||||
// line-height: 22px;
|
||||
// max-width: 125px;
|
||||
// margin-bottom: 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
// &__info {
|
||||
// display: flex;
|
||||
// justify-content: space-between;
|
||||
// align-items: center;
|
||||
|
||||
p {
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
color: #000000;
|
||||
margin-bottom: 0;
|
||||
// p {
|
||||
// font-weight: 700;
|
||||
// font-size: 12px;
|
||||
// line-height: 20px;
|
||||
// color: #000000;
|
||||
// margin-bottom: 0;
|
||||
|
||||
span {
|
||||
color: #52B709;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
&Link {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: #DDEEC6;
|
||||
border-radius: 50px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
// span {
|
||||
// color: #52B709;
|
||||
// font-weight: 700;
|
||||
// }
|
||||
// }
|
||||
// &Link {
|
||||
// width: 48px;
|
||||
// height: 48px;
|
||||
// background: #DDEEC6;
|
||||
// border-radius: 50px;
|
||||
// display: flex;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
.container {
|
||||
|
94
src/pages/ProfileCandidate/ProfileCandidate.js
Normal file
94
src/pages/ProfileCandidate/ProfileCandidate.js
Normal file
@ -0,0 +1,94 @@
|
||||
import React, { useState } from 'react'
|
||||
import './ProfileCandidate.scss'
|
||||
import { ProfileHeader } from "../../components/ProfileHeader/ProfileHeader";
|
||||
import { ProfileBreadcrumbs } from '../../components/ProfileBreadcrumbs/ProfileBreadcrumbs';
|
||||
import { CardControl } from '../../components/CardControl/CardControl';
|
||||
import settingIcon from "../../images/settingIcon.png"
|
||||
import reportsIcon from "../../images/reports.png"
|
||||
import noteIcon from "../../images/note.png"
|
||||
import questionIcon from "../../images/question.png"
|
||||
import medium_male from "../../images/medium_male.png"
|
||||
import { Footer } from '../../components/Footer/Footer';
|
||||
import { HeadBottom } from '../../components/features/Candidate-lk/HeadBottom';
|
||||
|
||||
export const ProfileCandidate = () => {
|
||||
const [candidatsCardsControl] = useState([
|
||||
{
|
||||
path: 'quiz',
|
||||
img: reportsIcon,
|
||||
title: 'Мои тесты',
|
||||
description: '<span>У вас 122 часа<br/></span>отработанных в этом месяце'
|
||||
},
|
||||
{
|
||||
path: 'profile/settings',
|
||||
img: settingIcon,
|
||||
title: 'Настройки аккаунта',
|
||||
description: 'Перейдите чтобы начать редактирование'
|
||||
}
|
||||
]
|
||||
)
|
||||
return (
|
||||
<div className='profile-candidate'>
|
||||
<ProfileHeader />
|
||||
<div className="profile-candidate__head-bottom bottom-head">
|
||||
<HeadBottom />
|
||||
|
||||
</div>
|
||||
<div className="profile-candidate__container">
|
||||
<ProfileBreadcrumbs links={[{ name: 'Главная', link: '/profile-candidate' }]} />
|
||||
<div className="profile-candidate__title main-title">Добрый день, <span>Дмитрий</span></div>
|
||||
<div className="profile-candidate__row">
|
||||
<div className="profile-candidate__tests">
|
||||
|
||||
<div className='info-candidate'>
|
||||
<div className="info-candidate__img">
|
||||
<img src={medium_male} alt="" />
|
||||
</div>
|
||||
<div className="info-candidate__info">
|
||||
<div className="info-candidate__title">Открыто {3} теста из {12}</div>
|
||||
<div className="info-candidate__decor"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="profile-candidate__cards">
|
||||
{
|
||||
candidatsCardsControl.map((item, index) => <CardControl
|
||||
description={item.description}
|
||||
img={item.img}
|
||||
path={item.path}
|
||||
title={item.title}
|
||||
key={index}
|
||||
/>)
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className="profile-candidate__instructions instructions-candidate">
|
||||
<div className="instructions-candidate__container">
|
||||
<div className="instructions-candidate___row">
|
||||
<div className="instructions-candidate__title">Интсрукция:</div>
|
||||
<div className="instructions-candidate__note">
|
||||
<img className='instructions-candidate__icon' src={noteIcon} alt="" />
|
||||
<div className="instructions-candidate__text">
|
||||
Для подтверждения своих знаний - пройдите тестирование во вкладке
|
||||
<span>“Мои тесты”</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="instructions-candidate___row">
|
||||
<div className="instructions-candidate__title">Зачем?</div>
|
||||
<div className="instructions-candidate__note">
|
||||
<img className='instructions-candidate__icon' src={questionIcon} alt="" />
|
||||
<div className="instructions-candidate__text">
|
||||
Тесты itguild предназначены для того, чтобы подтверждать навыки, которые вы указали у себя.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
}
|
166
src/pages/ProfileCandidate/ProfileCandidate.scss
Normal file
166
src/pages/ProfileCandidate/ProfileCandidate.scss
Normal file
@ -0,0 +1,166 @@
|
||||
.profile-candidate{
|
||||
background: #F1F1F1;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
font-family: "LabGrotesque", sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
//max-width: 1200px;
|
||||
&__container{
|
||||
max-width: 1160px;
|
||||
margin: 0 auto 42px auto;
|
||||
flex: 1 1 auto;
|
||||
padding: 0 15px;
|
||||
}
|
||||
&__title{
|
||||
margin: 0 0 31px 0;
|
||||
}
|
||||
&__row{
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
// &__column{
|
||||
// flex: 1 1 60%;
|
||||
// }
|
||||
&__tests{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
flex: 1 1 60%;
|
||||
gap: 30px;
|
||||
}
|
||||
&__cards{
|
||||
display: flex;
|
||||
gap: 29px;
|
||||
width: 100%;
|
||||
@media (max-width: 660px) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
&__head-bottom{
|
||||
margin: 0 0 27px 0;
|
||||
//max-width: 1160px;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
.instructions-candidate{
|
||||
flex: 1 1 auto;
|
||||
background: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
&__container{
|
||||
padding: 40px 18px 32px 36px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 29px;
|
||||
@media (max-width: 660px) {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
&__note{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 22px;
|
||||
&:not(:last-child){
|
||||
margin: 0 0 30px 0;
|
||||
}
|
||||
|
||||
}
|
||||
&__icon{
|
||||
width: 25px;
|
||||
height: 30px;
|
||||
}
|
||||
&__title{
|
||||
color: #52B709;
|
||||
font-weight: 900;
|
||||
font-size: 14px;
|
||||
line-height: 171%;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
&__text{
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 200%;
|
||||
color: #000000;
|
||||
max-width: 235px;
|
||||
span{
|
||||
color: #406128;
|
||||
font-weight: 700;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-candidate{
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 20px 45px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 47px;
|
||||
@media (max-width:560px) {
|
||||
padding: 20px;
|
||||
}
|
||||
&__img{
|
||||
flex: 0 0 88px;
|
||||
height: 88px;
|
||||
img{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
&__info{
|
||||
width: 100%;
|
||||
}
|
||||
&__title{
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 200%;
|
||||
color: #000000;
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
&__decor{
|
||||
background: #52B709;
|
||||
border-radius: 12px;
|
||||
height: 8px;
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-head{
|
||||
margin: 0 0 40px 0;
|
||||
height: 66px;
|
||||
background: #FFF;
|
||||
&__container{
|
||||
max-width: 1160px;
|
||||
margin: 0 auto;
|
||||
padding: 0 15px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
&__img{
|
||||
flex: 0 0 37px;
|
||||
height: 37px;
|
||||
img{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
&__title{
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
line-height: 178%;
|
||||
color: #807777;
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ import gitImgItem from "../../images/gitItemImg.png"
|
||||
|
||||
import {apiRequest} from "../../api/request";
|
||||
import {Navigate} from "react-router-dom";
|
||||
import { Navigation } from '../../components/Navigation/Navigation';
|
||||
|
||||
import './summary.scss'
|
||||
|
||||
@ -30,6 +31,7 @@ export const Summary = () => {
|
||||
return (
|
||||
<div className='summary'>
|
||||
<ProfileHeader/>
|
||||
<Navigation />
|
||||
<div className='container'>
|
||||
<div className='summary__content'>
|
||||
<ProfileBreadcrumbs links={[
|
||||
|
@ -24,6 +24,7 @@ import noProjects from "../../images/noProjects.png";
|
||||
import arrow from "../../images/arrowCalendar.png";
|
||||
|
||||
import "./tracker.scss";
|
||||
import { Navigation } from "../../components/Navigation/Navigation";
|
||||
|
||||
export const Tracker = () => {
|
||||
const [toggleTab, setToggleTab] = useState(1);
|
||||
@ -532,6 +533,7 @@ export const Tracker = () => {
|
||||
return (
|
||||
<div className="tracker">
|
||||
<ProfileHeader />
|
||||
<Navigation />
|
||||
<div className="container">
|
||||
<div className="tracker__content">
|
||||
<ProfileBreadcrumbs
|
||||
|
@ -16,6 +16,7 @@ import arrow from "../../images/right-arrow.png";
|
||||
import arrowSwitchDate from "../../images/arrowViewReport.png";
|
||||
|
||||
import './viewReport.scss'
|
||||
import { Navigation } from '../../components/Navigation/Navigation';
|
||||
|
||||
export const ViewReport = () => {
|
||||
if(localStorage.getItem('role_status') === '18') {
|
||||
@ -79,6 +80,7 @@ export const ViewReport = () => {
|
||||
return (
|
||||
<div className='viewReport'>
|
||||
<ProfileHeader/>
|
||||
<Navigation />
|
||||
<div className='container'>
|
||||
<div className='viewReport__info'>
|
||||
<ProfileBreadcrumbs links={[{name: 'Главная', link: '/profile'},
|
||||
|
@ -1,24 +0,0 @@
|
||||
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";
|
||||
|
||||
|
||||
export const InstructionPage = () => {
|
||||
|
||||
const test = useSelector(selectedTest)
|
||||
|
||||
let navigate = useNavigate();
|
||||
if (!test) {
|
||||
navigate('/quiz')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderPageTestsQuiz isVisibilityButton={false}/>
|
||||
<Instruction/>
|
||||
</>
|
||||
)
|
||||
};
|
@ -1,26 +0,0 @@
|
||||
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 {selectedTest, selectPassedTests} from "../../redux/quizSlice";
|
||||
|
||||
|
||||
export const InterjacentPage = () => {
|
||||
|
||||
const test = useSelector(selectedTest);
|
||||
const passedTests = useSelector(selectPassedTests)
|
||||
let navigate = useNavigate();
|
||||
if (!test) {
|
||||
navigate('/quiz')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderPageTestsQuiz isVisibilityButton={true}/>
|
||||
<MyTestsQuiz listTests={passedTests}/>
|
||||
</>
|
||||
)
|
||||
};
|
83
src/pages/quiz/PassingTests.js
Normal file
83
src/pages/quiz/PassingTests.js
Normal file
@ -0,0 +1,83 @@
|
||||
import React, { useState } from 'react'
|
||||
import { ProfileHeader } from '../../components/ProfileHeader/ProfileHeader'
|
||||
import { HeadBottom } from '../../components/features/Candidate-lk/HeadBottom'
|
||||
import { ProfileBreadcrumbs } from '../../components/ProfileBreadcrumbs/ProfileBreadcrumbs'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Footer } from '../../components/Footer/Footer'
|
||||
import { QuizPassingInformation } from '../../components/features/quiz/Quiz-passing-information'
|
||||
import { CardIntroduction } from '../../components/features/quiz/Card-introduction'
|
||||
import { TaskQuiz } from '../../components/features/quiz/Task'
|
||||
import { BlockCompletedTest } from '../../components/features/quiz/BlockCompletedTest'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { completedTestSelector, selectedTest } from '../../redux/quizSlice'
|
||||
|
||||
export const PassingTests = () => {
|
||||
//const selectedTest = useSelector(selectedTest)
|
||||
|
||||
if(''){
|
||||
|
||||
}
|
||||
|
||||
const time = new Date();
|
||||
time.setSeconds(time.getSeconds() + 600);//600 - кол-во секунд для прохождения теста
|
||||
|
||||
const [startTest, setStartTest] = useState(false)
|
||||
const completedTest = useSelector(completedTestSelector)
|
||||
|
||||
const introduction = [
|
||||
{
|
||||
title: 'Зачем?',
|
||||
description: 'Тесты itguild предназначены для того, чтобы подтверждать навыки, которые вы указали у себя.'
|
||||
},
|
||||
{
|
||||
title: 'Почему именно тестирование?',
|
||||
description: 'Тесты itguild заменяют первое техническое собеседование по любой вакансии.'
|
||||
},
|
||||
{
|
||||
title: 'Какие тесты нужно проходить?',
|
||||
description: 'Здесь все довольно просто — следует проходить тесты по инструментам и навыкам, которыми вы владеете.'
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<div className='passing-tests-page'>
|
||||
<ProfileHeader />
|
||||
<HeadBottom />
|
||||
<div className="passing-tests-page__container">
|
||||
<ProfileBreadcrumbs links={[
|
||||
{ name: 'Главная', link: '/profile-candidate' },
|
||||
{ name: 'Тестирование', link: '/quiz' },
|
||||
{ name: 'Прохождение тестов', link: '/quiz/test' },
|
||||
]} />
|
||||
<div className="passing-tests-page__title main-title">Тестирование в позиции Junior разработчик </div>
|
||||
<div className="passing-tests-page__passing-information">
|
||||
<QuizPassingInformation expiryTimestamp={time} setStartTest={setStartTest} />
|
||||
</div>
|
||||
|
||||
{
|
||||
!completedTest &&
|
||||
<>
|
||||
{startTest && <div className="passing-tests-page__block-green">Тестирование началось</div>}
|
||||
{startTest ? <TaskQuiz /> : <div className='passing-tests-page__introduction'>
|
||||
{
|
||||
introduction.map((item, i) => <CardIntroduction description={item.description} title={item.title} key={i} />)
|
||||
}
|
||||
</div>}
|
||||
{
|
||||
!startTest && <div className="passing-tests-page__block-text block-text">
|
||||
ИЛИ <Link to={''} >выполните тестове задание</Link> , без прохождения тестов
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
}
|
||||
{
|
||||
completedTest && <>
|
||||
<div className="passing-tests-page__block-green">Тестирование завершено</div>
|
||||
<BlockCompletedTest />
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,17 +1,144 @@
|
||||
import React from 'react'
|
||||
import {HeaderQuiz} from "../../components/features/quiz/HeaderQuiz"
|
||||
import {MyTestsQuiz} from "../../components/features/quiz/MyTestsQuiz"
|
||||
import {useSelector} from "react-redux";
|
||||
import {selectQuestionnairesOfUser} from "../../redux/quizSlice";
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { questionnairesSelector, setQuestionnaires } from "../../redux/quizSlice";
|
||||
import { ProfileHeader } from '../../components/ProfileHeader/ProfileHeader';
|
||||
import { HeadBottom } from '../../components/features/Candidate-lk/HeadBottom';
|
||||
import { ProfileBreadcrumbs } from '../../components/ProfileBreadcrumbs/ProfileBreadcrumbs';
|
||||
import './quiz-page.scss'
|
||||
import { SelectedCategory } from '../../components/features/quiz/SelectedCategory';
|
||||
import { Footer } from '../../components/Footer/Footer';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { CardAvailableTest } from '../../components/features/quiz/CardAviableTest';
|
||||
import { apiRequest } from '../../api/request';
|
||||
import CategoriesItem from '../../components/CategoriesItem/CategoriesItem';
|
||||
|
||||
import BackEndImg from "../../pages/PartnerСategories/images/personalBackEnd.png"
|
||||
import FrontendImg from "../../pages/PartnerСategories/images/PersonalFrontend.png"
|
||||
import ArchitectureImg from "../../pages/PartnerСategories/images/PersonalArchitecture.png"
|
||||
import DesignImg from "../../pages/PartnerСategories/images/PersonalDesign.png"
|
||||
import TestImg from "../../pages/PartnerСategories/images/PersonalTesters.png"
|
||||
import AdminImg from "../../pages/PartnerСategories/images/PersonalAdmin.png"
|
||||
import ManageImg from "../../pages/PartnerСategories/images/PersonalMng.png"
|
||||
import CopyImg from "../../pages/PartnerСategories/images/PersonalCopy.png"
|
||||
import SmmImg from "../../pages/PartnerСategories/images/PersonalSMM.png"
|
||||
|
||||
export const QuizPage = () => {
|
||||
|
||||
const allTests = useSelector(selectQuestionnairesOfUser)
|
||||
const questionnaires = useSelector(questionnairesSelector)
|
||||
const dispatch = useDispatch()
|
||||
const [personalInfoItems] = useState([
|
||||
{
|
||||
title: 'Backend разработчики',
|
||||
link: '/registration-candidate',
|
||||
description: 'Java PHP Python C# React Vue.js NodeJs Golang Ruby JavaScript',
|
||||
available: true,
|
||||
img: BackEndImg
|
||||
},
|
||||
{
|
||||
title: 'Frontend разработчики',
|
||||
link: '/registration-candidate',
|
||||
description: 'Java PHP Python C# React Vue.js NodeJs Golang Ruby JavaScript',
|
||||
available: true,
|
||||
img: FrontendImg
|
||||
},
|
||||
{
|
||||
title: 'Архитектура проектов',
|
||||
link: '/registration-candidate',
|
||||
description: 'Потоки данных ER ERP CRM CQRS UML BPMN',
|
||||
available: true,
|
||||
img: ArchitectureImg
|
||||
},
|
||||
{
|
||||
title: 'Дизайн проектов',
|
||||
link: '/registration-candidate',
|
||||
description: 'Java PHP Python C# React Vue.js NodeJs Golang Ruby JavaScript',
|
||||
available: true,
|
||||
img: DesignImg
|
||||
},
|
||||
{
|
||||
title: 'Тестирование проектов',
|
||||
link: '/registration-candidate',
|
||||
description: 'SQL Postman TestRail Kibana Ручное тестирование',
|
||||
available: false,
|
||||
img: TestImg
|
||||
},
|
||||
{
|
||||
title: 'Администрирование проектов',
|
||||
link: '/registration-candidate',
|
||||
description: 'DevOps ELK Kubernetes Docker Bash Apache Oracle Git',
|
||||
available: false,
|
||||
img: AdminImg
|
||||
},
|
||||
{
|
||||
title: 'Управление проектом',
|
||||
link: '/registration-candidate',
|
||||
description: 'Scrum Kanban Agile Miro CustDev',
|
||||
available: false,
|
||||
img: ManageImg
|
||||
},
|
||||
{
|
||||
title: 'Копирайтинг проектов',
|
||||
link: '/registration-candidate',
|
||||
description: 'Теги Заголовок H1 Дескриптор Абзац Сценарий',
|
||||
available: false,
|
||||
img: CopyImg
|
||||
},
|
||||
{
|
||||
title: 'Реклама и SMM',
|
||||
link: '/registration-candidate',
|
||||
description: 'Java PHP Python C# React Vue.js NodeJs Golang Ruby JavaScript',
|
||||
available: false,
|
||||
img: SmmImg
|
||||
},
|
||||
]);
|
||||
const userId = localStorage.getItem('id')
|
||||
const [selectedCategory, setsetSelectedCategory] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
apiRequest(`/user-questionnaire/questionnaires-list?user_id=${userId}`)
|
||||
.then(res => dispatch(setQuestionnaires(res)))
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderQuiz header={true}/>
|
||||
<MyTestsQuiz listTests={allTests}/>
|
||||
</>
|
||||
<div className='quiz-page'>
|
||||
<ProfileHeader />
|
||||
<HeadBottom />
|
||||
<div className="quiz-page__container">
|
||||
<ProfileBreadcrumbs links={[{ name: 'Главная', link: '/profile-candidate' }, { name: 'Тестирование', link: '/quiz' }]} />
|
||||
<div className="quiz-page__title main-title">{!selectedCategory ? 'Тестирование' : 'Замена специализации'}</div>
|
||||
{!selectedCategory && <>
|
||||
<div className="quiz-page__specialization">
|
||||
<SelectedCategory setSelectedCategory={setsetSelectedCategory} />
|
||||
</div>
|
||||
<div className="quiz-page__block">
|
||||
Доступные тесты
|
||||
</div>
|
||||
<div className="quiz-page__cards-test">
|
||||
{
|
||||
questionnaires.length ? questionnaires.map((item, index) => (
|
||||
<CardAvailableTest
|
||||
description={'Вы новичок с реальным опытом работы до 1 года, или без опыта'}
|
||||
path={'quiz/test'}
|
||||
status={item.status}
|
||||
title={item.questionnaire_title}
|
||||
passedTest={item.passedTest}
|
||||
key={index}
|
||||
/>)) : <h1>Анкет нет</h1>
|
||||
}
|
||||
</div>
|
||||
<div className="block-text">
|
||||
ИЛИ <Link to={''} >выполните тестове задание</Link> , без прохождения тестов
|
||||
</div>
|
||||
</>}
|
||||
|
||||
{selectedCategory && <div className="quiz-page__categories-items">
|
||||
{personalInfoItems.map((item, index) => {
|
||||
return <CategoriesItem link={item.link} key={index} title={item.title} img={item.img} skills={item.description} available={item.available} />
|
||||
})
|
||||
}
|
||||
</div>}
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
}
|
42
src/pages/quiz/QuizReportPage.js
Normal file
42
src/pages/quiz/QuizReportPage.js
Normal file
@ -0,0 +1,42 @@
|
||||
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";
|
||||
import { selectedTest } from "../../redux/quizSlice";
|
||||
import React from "react";
|
||||
import { ProfileHeader } from "../../components/ProfileHeader/ProfileHeader";
|
||||
import { HeadBottom } from "../../components/features/Candidate-lk/HeadBottom";
|
||||
import { ProfileBreadcrumbs } from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs";
|
||||
import { QuizReport } from "../../components/features/quiz/QuizReport";
|
||||
import { Footer } from "../../components/Footer/Footer";
|
||||
import suucessIcon from '../../images/quiz/success.png'
|
||||
import { AlertResult } from "../../components/features/quiz/AlertResult";
|
||||
|
||||
|
||||
export const QuizReportPage = () => {
|
||||
|
||||
const test = useSelector(selectedTest)
|
||||
|
||||
let navigate = useNavigate();
|
||||
if (!test) {
|
||||
navigate('/quiz')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="quiz-report-page">
|
||||
<ProfileHeader />
|
||||
<HeadBottom />
|
||||
<div className="quiz-report-page__container">
|
||||
<ProfileBreadcrumbs links={[{ name: 'Главная', link: '/profile-candidate' }, { name: 'Тестирование', link: '/quiz' },
|
||||
{ name: 'Отчет по тестированию', link: '/quiz/report' }]} />
|
||||
<div className="quiz-report-page__title main-title">Отчет по тестированию позиции Junior разработчик </div>
|
||||
<div className="quiz-report-page__report-quiz">
|
||||
<QuizReport />
|
||||
</div>
|
||||
|
||||
<AlertResult />
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
};
|
@ -1,24 +0,0 @@
|
||||
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";
|
||||
import {selectedTest} from "../../redux/quizSlice";
|
||||
import React, {useEffect} from "react";
|
||||
|
||||
export const QuizTestPage = () => {
|
||||
let navigate = useNavigate();
|
||||
const test = useSelector(selectedTest);
|
||||
|
||||
useEffect(() => {
|
||||
if (!test) {
|
||||
navigate('/quiz')
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderPageTestsQuiz isVisibilityButton={false}/>
|
||||
<TaskQuiz/>
|
||||
</>
|
||||
)
|
||||
};
|
@ -1,24 +0,0 @@
|
||||
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";
|
||||
import {selectedTest} from "../../redux/quizSlice";
|
||||
import React from "react";
|
||||
|
||||
|
||||
export const ResultPage = () => {
|
||||
|
||||
const test = useSelector(selectedTest)
|
||||
|
||||
let navigate = useNavigate();
|
||||
if (!test) {
|
||||
navigate('/quiz')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderPageTestsQuiz isVisibilityButton={false}/>
|
||||
<Results/>
|
||||
</>
|
||||
)
|
||||
};
|
143
src/pages/quiz/quiz-page.scss
Normal file
143
src/pages/quiz/quiz-page.scss
Normal file
@ -0,0 +1,143 @@
|
||||
.quiz-page{
|
||||
background: #F1F1F1;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
font-family: "LabGrotesque", sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&__container{
|
||||
max-width: 1160px;
|
||||
margin: 0 auto 42px auto;
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
padding: 0 15px;
|
||||
}
|
||||
&__title{
|
||||
margin: 0 0 39px 0;
|
||||
}
|
||||
&__block{
|
||||
background: #E1FCCF;
|
||||
border-radius: 12px 12px 0px 0px;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
line-height: 178%;
|
||||
color: #000000;
|
||||
padding: 20px 51px;
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
&__cards-test{
|
||||
display: grid;
|
||||
grid-gap: 29px;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
margin-bottom: 29px;
|
||||
@media (max-width: 955px) {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
@media (max-width: 668px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
}
|
||||
&__specialization{
|
||||
margin: 0 0 26px 0;
|
||||
}
|
||||
&__categories-items{
|
||||
display: grid;
|
||||
grid-gap: 24px;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
margin-bottom: 24px;
|
||||
@media (max-width: 955px) {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
@media (max-width: 668px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
.interjacent-page-quiz{
|
||||
|
||||
background: #F1F1F1;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
font-family: "LabGrotesque", sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&__title{
|
||||
margin: 0 0 39px 0;
|
||||
}
|
||||
&__passing-information{
|
||||
margin: 0 0 32px 0;
|
||||
}
|
||||
&__container{
|
||||
max-width: 1160px;
|
||||
margin: 0 auto 42px auto;
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
padding: 0 15px;
|
||||
}
|
||||
&__introduction-items{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0px -25px -50px;
|
||||
margin: 0 auto 54px auto;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.passing-tests-page{
|
||||
background: #F1F1F1;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
font-family: "LabGrotesque", sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&__container{
|
||||
max-width: 1160px;
|
||||
margin: 0 auto 42px auto;
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
padding: 0 15px;
|
||||
}
|
||||
&__title{
|
||||
margin: 0 0 39px 0;
|
||||
}
|
||||
&__passing-information{
|
||||
margin: 0 0 29px 0;
|
||||
}
|
||||
&__block-green{
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
line-height: 178%;
|
||||
color: #000000;
|
||||
background: #E1FCCF;
|
||||
border-radius: 12px 12px 0px 0px;
|
||||
padding: 20px 51px;
|
||||
}
|
||||
&__introduction{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.quiz-report-page{
|
||||
background: #F1F1F1;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
font-family: "LabGrotesque", sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&__container{
|
||||
max-width: 1160px;
|
||||
margin: 0 auto 42px auto;
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
padding: 0 15px;
|
||||
}
|
||||
&__title{
|
||||
margin: 0 0 39px 0;
|
||||
}
|
||||
&__report-quiz{
|
||||
margin: 0 0 28px 0;
|
||||
}
|
||||
}
|
@ -4,14 +4,76 @@ import {apiRequest} from "../api/request";
|
||||
|
||||
|
||||
const initialState = {
|
||||
// questions: [],
|
||||
answer: [],
|
||||
answers: [
|
||||
{
|
||||
"id": "12",
|
||||
"question_id": "7",
|
||||
"answer_body": "Нsdf sfd fds sdf sf sfsdf sdfеск вsdffsdfsdf sf sdf sdfsdfsdfsdfdsjknsdkf dssdjf sdfbsdhf sd hjdsfv sdhjvар1 отв1 истина"
|
||||
},
|
||||
{
|
||||
"id": "23",
|
||||
"question_id": "7",
|
||||
"answer_body": "Неск вар1 отв1 истина"
|
||||
},
|
||||
{
|
||||
"id": "233",
|
||||
"question_id": "7",
|
||||
"answer_body": "lorem sdfdsf dfs sdf "
|
||||
},
|
||||
],
|
||||
questionnaires: [],
|
||||
questions: [
|
||||
{
|
||||
"id": "4",
|
||||
"question_type_id": "3",
|
||||
"question_body": "Для чего в Python используется встроенная функция enumerate()?",
|
||||
"question_priority": null,
|
||||
"next_question": null,
|
||||
"time_limit": "00:22:00"
|
||||
},
|
||||
{
|
||||
"id": "24",
|
||||
"question_type_id": "3",
|
||||
"question_body": "Для чего в Python dfsf троенная функция enumerate()?",
|
||||
"question_priority": null,
|
||||
"next_question": null,
|
||||
"time_limit": "00:22:00"
|
||||
},
|
||||
{
|
||||
"id": "41",
|
||||
"question_type_id": "3",
|
||||
"question_body": "Для чегоsdfsdfя функция enumerate()?",
|
||||
"question_priority": null,
|
||||
"next_question": null,
|
||||
"time_limit": "00:22:00"
|
||||
},
|
||||
{
|
||||
"id": "5",
|
||||
"question_type_id": '2',
|
||||
"question_body": "Один ответ2",
|
||||
"question_priority": null,
|
||||
"next_question": null,
|
||||
"time_limit": "00:22:00"
|
||||
}
|
||||
],
|
||||
selectedTest: {
|
||||
"user_id": 1,
|
||||
"uuid": "d222f858-60fd-47fb-8731-dc9d5fc384c5",
|
||||
"score": 11,
|
||||
"status": 2,
|
||||
"percent_correct_answers": 0.25,
|
||||
"testing_date": "2022-03-17 11:14:22",
|
||||
"questionnaire_title": "Кат1 Анкета 1 активна"
|
||||
},
|
||||
selectedAnswers:{},
|
||||
completedTest: false,
|
||||
result: null,
|
||||
isLoading: false,
|
||||
dataQuestionnairesOfUser: [],
|
||||
passedTests: [],
|
||||
selectedTest: null,
|
||||
userInfo: null
|
||||
|
||||
userInfo: null,
|
||||
|
||||
};
|
||||
export const setUserInfo = createAsyncThunk(
|
||||
'userInfo',
|
||||
@ -47,13 +109,15 @@ export const quizSlice = createSlice({
|
||||
name: 'quiz',
|
||||
initialState,
|
||||
reducers: {
|
||||
setQuestionnairesList: (state, action) => {
|
||||
state.dataQuestionnairesOfUser = action.payload;
|
||||
state.passedTests = action.payload.filter(item => item.status === 2)
|
||||
setQuestionnaires: (state, action) => {
|
||||
state.questionnaires = action.payload;
|
||||
},
|
||||
setSelectedTest: (state, action) => {
|
||||
state.selectedTest = action.payload
|
||||
},
|
||||
setCompleteTest: (state, action) => {
|
||||
state.completedTest = true
|
||||
}
|
||||
},
|
||||
extraReducers: {
|
||||
[setUserInfo.fulfilled]: (state, action) => {
|
||||
@ -68,16 +132,21 @@ export const quizSlice = createSlice({
|
||||
},
|
||||
});
|
||||
|
||||
export const {setQuestionnairesList, setSelectedTest} = quizSlice.actions;
|
||||
export const {setQuestionnaires, setSelectedTest, setCompleteTest} = quizSlice.actions;
|
||||
|
||||
|
||||
export const selectAnswer = (state) => state.quiz.answer;
|
||||
export const selectQuestionnairesOfUser = (state) => state.quiz.dataQuestionnairesOfUser;
|
||||
export const answersSelector = (state) => state.quiz.answers;
|
||||
export const questionnairesSelector = (state) => state.quiz.questionnaires;
|
||||
export const selectedAnswersSelector = (state) => state.quiz.selectedAnswers
|
||||
export const questionsSelector = (state) => state.quiz.questions
|
||||
export const completedTestSelector = (state) => state.quiz.completedTest
|
||||
|
||||
export const selectResult = (state) => state.quiz.result;
|
||||
export const selectedTest = (state) => state.quiz.selectedTest;
|
||||
export const selectPassedTests = (state) => state.quiz.passedTests;
|
||||
export const selectUserInfo = (state) => state.quiz.userInfo;
|
||||
|
||||
|
||||
|
||||
export default quizSlice.reducer;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user