@@ -36,6 +36,10 @@ export const Navigation = () => {
|
||||
path: "/payouts",
|
||||
name: "Выплаты",
|
||||
},
|
||||
{
|
||||
path: "/quiz",
|
||||
name: "Тесты",
|
||||
},
|
||||
{
|
||||
path: "/settings",
|
||||
name: "Настройки",
|
||||
@@ -91,7 +95,11 @@ export const Navigation = () => {
|
||||
<nav className="profileHeader__nav">
|
||||
{navInfo[user].map((link, index) => {
|
||||
return (
|
||||
<NavLink key={index} end to={`/profile${link.path}`}>
|
||||
<NavLink
|
||||
key={index}
|
||||
end
|
||||
to={link.path === "/quiz" ? link.path : `/profile${link.path}`}
|
||||
>
|
||||
{link.name}
|
||||
</NavLink>
|
||||
);
|
||||
|
@@ -2,7 +2,7 @@ import React from "react";
|
||||
|
||||
import suucessIcon from "assets/images/quiz/success.png";
|
||||
|
||||
export const AlertResult = () => {
|
||||
export const AlertResult = ({ info }) => {
|
||||
const successTest = false;
|
||||
|
||||
return (
|
||||
@@ -17,7 +17,7 @@ export const AlertResult = () => {
|
||||
className="alert-result__text"
|
||||
style={{ color: successTest ? "#52B709" : "#5B6871" }}
|
||||
>
|
||||
Благодарим Вас за прохождение теста "Junior разработчик". Ваши
|
||||
Благодарим Вас за прохождение теста "{info.questionnaire_title}". Ваши
|
||||
результаты проверены, готовы пригласить Вас в команду
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -5,16 +5,19 @@ import StarRating from "@components/StarRating/StarRating";
|
||||
|
||||
import rightArrow from "assets/icons/arrows/arrowRight.svg";
|
||||
|
||||
export const CardAvailableTest = ({ title, description, path, passedTest }) => {
|
||||
export const CardAvailableTest = ({ title, description, path, status }) => {
|
||||
return (
|
||||
<div className="card-available-test">
|
||||
<Link
|
||||
to={`/${path}`}
|
||||
to={status === 2 ? `/quiz/report/${path}` : `/quiz/test/${path}`}
|
||||
aria-disabled={true}
|
||||
className="card-available-test__container"
|
||||
style={{
|
||||
opacity: passedTest ? 0.3 : 1,
|
||||
pointerEvents: passedTest ? "none" : "all",
|
||||
}}
|
||||
style={
|
||||
{
|
||||
// opacity: status !== 1 ? 0.3 : 1,
|
||||
// pointerEvents: status !== 1 ? "none" : "all",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div className="card-available-test__top-head">
|
||||
<StarRating />
|
||||
@@ -27,11 +30,10 @@ export const CardAvailableTest = ({ title, description, path, passedTest }) => {
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{passedTest && (
|
||||
{status === 2 && (
|
||||
<div className="card-available-test__finished">
|
||||
<p>Получить отчет по тестированию</p>
|
||||
<Link to={"/quiz/report"}>Отчет по тесту</Link>
|
||||
<Link to={`/quiz/report/${path}`}>Отчет по тесту</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@@ -1,44 +1,18 @@
|
||||
import React from "react";
|
||||
|
||||
export const GetOptionTask = ({ type, answer, handleChange, inputValue }) => {
|
||||
switch (type) {
|
||||
case "1":
|
||||
return (
|
||||
<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="radio"
|
||||
value={answer.answer_body}
|
||||
name={"radio"}
|
||||
id={answer.id}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<label htmlFor={answer.id}>{answer.answer_body}</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export const GetOptionTask = ({ type, answer, handleChange }) => {
|
||||
const { answer_body, id } = answer;
|
||||
return (
|
||||
<div className="form-task__group" key={id}>
|
||||
<input
|
||||
className="form-task__check"
|
||||
type={+type === 3 ? "checkbox" : "radio"}
|
||||
value={answer_body}
|
||||
name={+type === 3 ? "checkbox" : "radio"}
|
||||
id={id}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<label htmlFor={id}>{answer_body}</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect } from "react";
|
||||
import React from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
import { selectUserInfo, setUserInfo } from "@redux/quizSlice";
|
||||
import { selectUserInfo } from "@redux/quizSlice";
|
||||
|
||||
import { urlForLocal } from "@utils/helper";
|
||||
|
||||
@@ -13,15 +13,6 @@ export const HeaderQuiz = ({ header }) => {
|
||||
const userId = localStorage.getItem("id");
|
||||
const userInfo = useSelector(selectUserInfo);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setUserInfo(userId));
|
||||
}, [userId, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
// apiRequest(`/user-questionnaire/questionnaires-list?user_id=${userId}`)
|
||||
// .then(res => dispatch(setQuestionnairesList(res)))
|
||||
}, [userId, dispatch]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{userInfo?.status === 500 ? (
|
||||
|
@@ -1,36 +1,71 @@
|
||||
import moment from "moment";
|
||||
import React, { useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useTimer } from "react-timer-hook";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { completedTestSelector } from "@redux/quizSlice";
|
||||
import { setQuestions } from "@redux/quizSlice";
|
||||
|
||||
import { apiRequest } from "@api/request";
|
||||
|
||||
import { useNotification } from "@hooks/useNotification";
|
||||
|
||||
import StarRating from "@components/StarRating/StarRating";
|
||||
|
||||
import accempt from "assets/images/quiz/accempt.png";
|
||||
import timer from "assets/images/quiz/timer.png";
|
||||
import iconTomer from "assets/images/quiz/timer.png";
|
||||
|
||||
export const QuizPassingInformation = ({ expiryTimestamp, setStartTest }) => {
|
||||
const { seconds, minutes, isRunning, start, restart } = useTimer({
|
||||
expiryTimestamp,
|
||||
autoStart: false,
|
||||
onExpire: () => {
|
||||
console.warn("onExpire called");
|
||||
},
|
||||
});
|
||||
const completedTest = useSelector(completedTestSelector);
|
||||
export const QuizPassingInformation = ({ setStartTest, uuid, timer }) => {
|
||||
const { restart, pause, hours, minutes, seconds, isRunning } = timer;
|
||||
const navigate = useNavigate();
|
||||
const { showNotification } = useNotification();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const startTesting = () => {
|
||||
setStartTest(true);
|
||||
start();
|
||||
apiRequest(`/question/get-questions?uuid=${uuid}`)
|
||||
.then((res) => {
|
||||
if (res.status === 400) {
|
||||
dispatch(setQuestions(null));
|
||||
showNotification({
|
||||
show: true,
|
||||
text: res?.message || "",
|
||||
type: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
dispatch(setQuestions(res));
|
||||
setStartTest(true);
|
||||
restart(
|
||||
moment()
|
||||
.add(res[0]?.time_limit.split(":")[0], "hours")
|
||||
.add(res[0]?.time_limit.split(":")[1], "minutes")
|
||||
.add(res[0]?.time_limit.split(":")[2], "seconds"),
|
||||
true
|
||||
);
|
||||
})
|
||||
.catch((e) => {
|
||||
dispatch(setQuestions(null));
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (completedTest) {
|
||||
const time = new Date();
|
||||
time.setSeconds(time.getSeconds() + 0); //600 - кол-во секунд для прохождения теста
|
||||
restart(time, false);
|
||||
}
|
||||
}, [completedTest]);
|
||||
const checkTest = () =>
|
||||
apiRequest(
|
||||
`user-questionnaire/questionnaire-completed?user_questionnaire_uuid=${uuid}`
|
||||
);
|
||||
|
||||
const completeTest = () =>
|
||||
apiRequest("/user-questionnaire/questionnaire-completed", {
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
const finishQuiz = async () => {
|
||||
Promise.all([checkTest, completeTest])
|
||||
.then(function () {
|
||||
pause();
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="quiz-passing-information">
|
||||
@@ -48,32 +83,38 @@ export const QuizPassingInformation = ({ expiryTimestamp, setStartTest }) => {
|
||||
разработчик
|
||||
</div>
|
||||
</div>
|
||||
<div className="quiz-passing-information__timer timer-quiz">
|
||||
<div className="quiz-passing-information__icon">
|
||||
<img src={timer} alt="" />
|
||||
{isRunning && (
|
||||
<div className="quiz-passing-information__timer timer-quiz">
|
||||
<div className="quiz-passing-information__icon">
|
||||
<img src={iconTomer} alt="" />
|
||||
</div>
|
||||
<div className="quiz-passing-information__text">
|
||||
Время на прохождение теста:
|
||||
<br />
|
||||
<span>
|
||||
{hours.toString().padStart(2, "0") +
|
||||
":" +
|
||||
minutes.toString().padStart(2, "0") +
|
||||
":" +
|
||||
seconds.toString().padStart(2, "0")}
|
||||
секунд
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="quiz-passing-information__text">
|
||||
{completedTest ? "Время вышло" : "Время на прохождение теста:"}{" "}
|
||||
<br />
|
||||
<span>
|
||||
{minutes.toString().padStart(2, "0") +
|
||||
":" +
|
||||
seconds.toString().padStart(2, "0")}{" "}
|
||||
секунд
|
||||
</span>
|
||||
)}
|
||||
{!isRunning && (
|
||||
<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>
|
||||
<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 && (
|
||||
{!isRunning && (
|
||||
<button
|
||||
className="quiz-passing-information__button btn-green"
|
||||
onClick={startTesting}
|
||||
@@ -82,9 +123,15 @@ export const QuizPassingInformation = ({ expiryTimestamp, setStartTest }) => {
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{isRunning && (
|
||||
<button
|
||||
className="quiz-passing-information__button quiz-btn"
|
||||
onClick={finishQuiz}
|
||||
>
|
||||
Завершить
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* {isRunning && <button className="quiz-passing-information__button quiz-btn" onClick={pause}>Завершить</button>} */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@@ -2,7 +2,13 @@ import React from "react";
|
||||
|
||||
import StarRating from "@components/StarRating/StarRating";
|
||||
|
||||
export const QuizReport = () => {
|
||||
export const QuizReport = ({ info }) => {
|
||||
function correctAnswers() {
|
||||
return info.number_questions * info.percent_correct_answers;
|
||||
}
|
||||
function correctWrongAnswers() {
|
||||
return info.number_questions * (1 - info.percent_correct_answers);
|
||||
}
|
||||
return (
|
||||
<div className="report">
|
||||
<div className="report__row">
|
||||
@@ -13,16 +19,18 @@ export const QuizReport = () => {
|
||||
countActiveStars={0.5}
|
||||
size={61}
|
||||
/>
|
||||
<div className="report__job-title">
|
||||
Junior <br /> разработчик
|
||||
</div>
|
||||
<div className="report__job-title">{info.questionnaire_title}</div>
|
||||
</div>
|
||||
<div className="report__column">
|
||||
<div className="report__value">22</div>
|
||||
<div className="report__value">
|
||||
{Boolean(correctAnswers()) ? correctAnswers() : 0}
|
||||
</div>
|
||||
<div className="report__text">Правильных ответов</div>
|
||||
</div>
|
||||
<div className="report__column">
|
||||
<div className="report__value report__value_false">02</div>
|
||||
<div className="report__value report__value_false">
|
||||
{Boolean(correctWrongAnswers()) ? correctWrongAnswers() : 0}
|
||||
</div>
|
||||
<div className="report__text">Не правильных ответов</div>
|
||||
</div>
|
||||
<div className="report__column">
|
||||
|
@@ -1,148 +1,158 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
|
||||
import {
|
||||
answersSelector,
|
||||
fetchGetAnswers, // fetchUserAnswerOne,
|
||||
// fetchUserAnswersMany,
|
||||
questionsSelector,
|
||||
selectedTest, // setAnswers,
|
||||
setCompleteTest,
|
||||
} from "@redux/quizSlice";
|
||||
import { questionsSelector } from "@redux/quizSlice";
|
||||
|
||||
import { apiRequest } from "@api/request";
|
||||
|
||||
import { useHandlerFieldTest } from "@hooks/useHandlerFieldTest";
|
||||
import { useNotification } from "@hooks/useNotification";
|
||||
|
||||
import { Loader } from "@components/Common/Loader/Loader";
|
||||
|
||||
import questionIcon from "assets/images/question.png";
|
||||
|
||||
import { GetOptionTask } from "./GetOptionTask";
|
||||
// import { HeaderQuiz } from "./HeaderQuiz";
|
||||
// import { Progressbar } from "./ProgressbarQuiz";
|
||||
import "./quiz.scss";
|
||||
|
||||
export const TaskQuiz = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const answers = useSelector(answersSelector);
|
||||
export const TaskQuiz = ({ timer }) => {
|
||||
const navigate = useNavigate();
|
||||
const { restart } = timer;
|
||||
const { uuid } = useParams();
|
||||
const userId = localStorage.getItem("id");
|
||||
const questions = useSelector(questionsSelector);
|
||||
|
||||
const dataTest = useSelector(selectedTest);
|
||||
const [index, setIndex] = useState(0);
|
||||
const [checkedValues, setCheckedValues] = useState([]);
|
||||
//const [stripValue, setStripValue] = useState(0);
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
const [isLoadingSendAnswers] = useState(false);
|
||||
const { showNotification } = useNotification();
|
||||
|
||||
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) => {
|
||||
dispatch(fetchGetAnswers(response[0].id));
|
||||
setStripValue(((+index + 1) * 100) / response.length);
|
||||
}
|
||||
);
|
||||
}, [dispatch]);
|
||||
const { userResponses, handleChange } = useHandlerFieldTest({
|
||||
questions,
|
||||
indexQuestion: index,
|
||||
});
|
||||
|
||||
const nextQuestion = async (e) => {
|
||||
e.preventDefault();
|
||||
//Проверка на валидацию ответов
|
||||
if (!(checkedValues.length || inputValue)) {
|
||||
//Проверка на существование овтетов
|
||||
if (!userResponses[index]) {
|
||||
alert("Вы не ответили на вопрос");
|
||||
return;
|
||||
}
|
||||
|
||||
//отправка ответов на сервер
|
||||
if (questions[index].question_type_id != 3) {
|
||||
//dispatch(fetchUserAnswerOne(checkedValues));
|
||||
} else {
|
||||
console.log(checkedValues);
|
||||
// dispatch(fetchUserAnswersMany(checkedValues));
|
||||
}
|
||||
// setLoadingSendAnswers(true);
|
||||
// .finally(() => setLoadingSendAnswers(false));
|
||||
|
||||
//Проверка на окончание теста
|
||||
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;
|
||||
|
||||
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,
|
||||
// отправка ответов на сервер
|
||||
if (questions.length === userResponses.length) {
|
||||
await apiRequest(`/user-response/set-responses`, {
|
||||
method: "POST",
|
||||
data: {
|
||||
user_id: userId,
|
||||
user_questionnaire_uuid: uuid,
|
||||
userResponses: JSON.stringify(userResponses),
|
||||
},
|
||||
]);
|
||||
return;
|
||||
}).then(() => {
|
||||
showNotification({
|
||||
show: true,
|
||||
text: "Тест успешно пройден",
|
||||
type: "success",
|
||||
});
|
||||
navigate("/quiz");
|
||||
// if (String(res?.status)[0] !== "2") {
|
||||
// showNotification({
|
||||
// show: true,
|
||||
// text: res?.message || "",
|
||||
// type: "error",
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
});
|
||||
// .catch((e) => {
|
||||
// showNotification({
|
||||
// show: true,
|
||||
// text: e?.message || "",
|
||||
// type: "error",
|
||||
// });
|
||||
// })
|
||||
}
|
||||
|
||||
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),
|
||||
]);
|
||||
//установка таймера на вопрос если он существует
|
||||
if (questions[index + 1]?.time_limit !== "00:00:00") setValueTimer();
|
||||
|
||||
// переход на следующий вопрос
|
||||
setIndex((prev) => (questions[prev + 1] ? prev + 1 : prev));
|
||||
};
|
||||
|
||||
console.log("render task");
|
||||
const complete = (e) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const setValueTimer = () => {
|
||||
const time_limit = questions[index + 1].time_limit.split(":");
|
||||
restart(
|
||||
moment()
|
||||
.add(time_limit[0], "hours")
|
||||
.add(time_limit[1], "minutes")
|
||||
.add(time_limit[2], "seconds")
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="task">
|
||||
{
|
||||
{questions ? (
|
||||
<div className="task__container">
|
||||
<div className="task__header">
|
||||
<img src={questionIcon} alt="" />
|
||||
<img src={questionIcon} alt="questionIcon" />
|
||||
<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}
|
||||
/>
|
||||
))}
|
||||
<form
|
||||
className="task__form form-task"
|
||||
onSubmit={
|
||||
index !== questions.length - 1 ? nextQuestion : complete
|
||||
}
|
||||
>
|
||||
{questions[index].question_type_id === 1 ? (
|
||||
<div className="form-task__group">
|
||||
<textarea
|
||||
className="form-task__field"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
questions[index]?.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>
|
||||
)}
|
||||
<button
|
||||
onClick={nextQuestion}
|
||||
// disabled={isLoadingSendAnswers}
|
||||
className="form-task__btn quiz-btn"
|
||||
>
|
||||
{isLoadingSendAnswers ? (
|
||||
<Loader width={25} height={25} />
|
||||
) : index !== questions.length - 1 ? (
|
||||
"Далее"
|
||||
) : (
|
||||
"Завершить"
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
) : (
|
||||
<h1>ОШибка</h1>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
Reference in New Issue
Block a user