quiz + lk-candidate

This commit is contained in:
2023-04-19 20:22:06 +03:00
parent 802e48eb1f
commit f2d7730214
60 changed files with 2272 additions and 812 deletions

View 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>
)
}

View 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>
)
}

View 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>
)
}

View 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>
)
}

View 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>
)
}

View File

@ -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>
)
}
}

View File

@ -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 (

View 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>
)
}

View 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>
)
}

View 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>
)
}

View File

@ -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