Merge remote-tracking branch 'origin/lk_candidate' into candidate-area
# Conflicts: # src/App.js # src/components/Footer/footer.scss # src/components/ProfileHeader/ProfileHeader.js # src/pages/AuthForCandidate/authForCandidate.scss
This commit is contained in:
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,8 +1,3 @@
|
||||
footer {
|
||||
border-top: 1px solid #EBEBEB;
|
||||
padding: 35px 0 50px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
|
||||
&__top {
|
||||
|
101
src/components/Navigation/Navigation.js
Normal file
101
src/components/Navigation/Navigation.js
Normal file
@ -0,0 +1,101 @@
|
||||
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 } from '../../redux/outstaffingSlice';
|
||||
|
||||
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) : ""} 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>
|
||||
|
@ -10,12 +10,11 @@ import {getRole} from "../../redux/roleSlice";
|
||||
import {urlForLocal} from "../../helper";
|
||||
|
||||
import avatarMok from "../../pages/PartnerTreaties/Images/avatarMok.png"
|
||||
|
||||
import { Navigation } from '../Navigation/Navigation';
|
||||
import './profileHeader.scss'
|
||||
|
||||
|
||||
|
||||
|
||||
export const ProfileHeader = () => {
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
|
@ -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
Reference in New Issue
Block a user