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

10
package-lock.json generated
View File

@ -48,6 +48,7 @@
"react-select": "^5.7.0", "react-select": "^5.7.0",
"react-slick": "^0.29.0", "react-slick": "^0.29.0",
"react-syntax-highlighter": "^15.4.5", "react-syntax-highlighter": "^15.4.5",
"react-timer-hook": "^3.0.5",
"react-yandex-metrika": "^2.6.0", "react-yandex-metrika": "^2.6.0",
"redux-devtools-extension": "^2.13.9", "redux-devtools-extension": "^2.13.9",
"resolve": "1.18.1", "resolve": "1.18.1",
@ -21571,6 +21572,15 @@
"react": ">= 0.14.0" "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": { "node_modules/react-transition-group": {
"version": "4.4.5", "version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",

View File

@ -44,6 +44,7 @@
"react-select": "^5.7.0", "react-select": "^5.7.0",
"react-slick": "^0.29.0", "react-slick": "^0.29.0",
"react-syntax-highlighter": "^15.4.5", "react-syntax-highlighter": "^15.4.5",
"react-timer-hook": "^3.0.5",
"react-yandex-metrika": "^2.6.0", "react-yandex-metrika": "^2.6.0",
"redux-devtools-extension": "^2.13.9", "redux-devtools-extension": "^2.13.9",
"resolve": "1.18.1", "resolve": "1.18.1",

View File

@ -12,10 +12,7 @@ import {ProfileCalendar} from "./components/ProfileCalendar/ProfileCalendar";
import FormPage from './pages/FormPage/FormPage.js' import FormPage from './pages/FormPage/FormPage.js'
import SingleReportPage from './pages/SingleReportPage/SingleReportPage' import SingleReportPage from './pages/SingleReportPage/SingleReportPage'
import {QuizPage} from './pages/quiz/QuizPage' import {QuizPage} from './pages/quiz/QuizPage'
import {InterjacentPage} from './pages/quiz/InterjacentPage' import {QuizReportPage} from './pages/quiz/QuizReportPage'
import {QuizTestPage} from './pages/quiz/QuizTestPage'
import {InstructionPage} from './pages/quiz/InstructionPage'
import {ResultPage} from './pages/quiz/ResultPage'
import {Profile} from './pages/Profile/Profile.js' import {Profile} from './pages/Profile/Profile.js'
import {Summary} from './pages/Summary/Summary' import {Summary} from './pages/Summary/Summary'
import {ViewReport} from './pages/ViewReport/ViewReport' import {ViewReport} from './pages/ViewReport/ViewReport'
@ -30,9 +27,11 @@ import {PartnerTreaties} from "./pages/PartnerTreaties/PartnerTreaties";
import {PartnerEmployees} from "./pages/PartnerEmployees/PartnerEmployees"; import {PartnerEmployees} from "./pages/PartnerEmployees/PartnerEmployees";
import {AuthForCandidate} from "./pages/AuthForCandidate/AuthForCandidate"; import {AuthForCandidate} from "./pages/AuthForCandidate/AuthForCandidate";
import {RegistrationForCandidate} from "./pages/RegistrationForCandidate/RegistrationForCandidate"; import {RegistrationForCandidate} from "./pages/RegistrationForCandidate/RegistrationForCandidate";
import './assets/global.scss'
import './fonts/stylesheet.css' import './fonts/stylesheet.css'
import 'bootstrap/dist/css/bootstrap.min.css' import 'bootstrap/dist/css/bootstrap.min.css'
import { ProfileCandidate } from './pages/ProfileCandidate/ProfileCandidate';
import { PassingTests } from './pages/quiz/PassingTests';
@ -58,10 +57,8 @@ const App = () => {
<Route exact path='quiz'> <Route exact path='quiz'>
<Route index element={<QuizPage/>}/> <Route index element={<QuizPage/>}/>
<Route exact path='interjacent' element={<InterjacentPage/>}/> <Route exact path='test' element={<PassingTests />}/>
<Route exact path='test' element={<QuizTestPage/>}/> <Route exact path='report' element={<QuizReportPage/>}/>
<Route exact path='instruction' element={<InstructionPage/>}/>
<Route exact path='result' element={<ResultPage/>}/>
</Route> </Route>
<Route exact path='profile'> <Route exact path='profile'>
@ -80,6 +77,9 @@ const App = () => {
<Route exact path='treaties' element={<PartnerTreaties/>}/> <Route exact path='treaties' element={<PartnerTreaties/>}/>
<Route exact path='categories/employees' element={<PartnerEmployees/>}/> <Route exact path='categories/employees' element={<PartnerEmployees/>}/>
</Route> </Route>
<Route exact path='profile-candidate/:id'>
<Route index element={<ProfileCandidate />}/>
</Route>
<Route path="*" element={<Navigate to="/auth" replace/>}/> <Route path="*" element={<Navigate to="/auth" replace/>}/>
</Routes> </Routes>

25
src/assets/global.scss Normal file
View 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;
}

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

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

View File

@ -2,7 +2,6 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 33px 32px 25px 28px; padding: 33px 32px 25px 28px;
width: 32%;
background: #FFFFFF; background: #FFFFFF;
border-radius: 12px; border-radius: 12px;
transition: all 0.3s ease; transition: all 0.3s ease;

View File

@ -1,5 +1,5 @@
.footer { .footer {
margin-top: -3rem; //margin-top: -3rem;
&__left { &__left {
display: flex; display: flex;
@ -74,7 +74,7 @@
@media (max-width: 575.98px) { @media (max-width: 575.98px) {
.footer { .footer {
&__left { &__left {
margin-top: 80px; // margin-top: 80px;
} }
&__description { &__description {

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

View File

@ -19,6 +19,7 @@ import {getRequestDates, setReportDate, setRequestDate} from "../../redux/report
import 'moment/locale/ru' import 'moment/locale/ru'
import './profileCalendar.scss' import './profileCalendar.scss'
import { Navigation } from '../Navigation/Navigation';
export const ProfileCalendar = () => { export const ProfileCalendar = () => {
@ -65,6 +66,7 @@ export const ProfileCalendar = () => {
return ( return (
<div className='profile__calendar'> <div className='profile__calendar'>
<ProfileHeader/> <ProfileHeader/>
<Navigation />
<div className='container'> <div className='container'>
<ProfileBreadcrumbs links={[{name: 'Главная', link: '/profile'},{name: 'Ваша отчетность', link: '/profile/calendar'}]} /> <ProfileBreadcrumbs links={[{name: 'Главная', link: '/profile'},{name: 'Ваша отчетность', link: '/profile/calendar'}]} />
<h2 className='summary__title'>Ваши отчеты</h2> <h2 className='summary__title'>Ваши отчеты</h2>

View File

@ -10,6 +10,7 @@ import {getRole} from "../../redux/roleSlice";
import {urlForLocal} from "../../helper"; import {urlForLocal} from "../../helper";
import './profileHeader.scss' import './profileHeader.scss'
import { Navigation } from '../Navigation/Navigation';
@ -20,58 +21,8 @@ export const ProfileHeader = () => {
const userRole = useSelector(getRole); const userRole = useSelector(getRole);
const profileInfo = useSelector(getProfileInfo);
const [user] = useState(localStorage.getItem('role_status') === '18' ? 'partner' : 'developer') 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); const [isLoggingOut, setIsLoggingOut] = useState(false);
@ -111,29 +62,6 @@ export const ProfileHeader = () => {
</button> </button>
</div> </div>
</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) : ""} className='profileHeader__personalInfoAvatar' alt='avatar'/>
</NavLink>
</div>
</div>
</div>
</header> </header>
) )
}; };

View File

@ -22,6 +22,7 @@ import arrow from "../../images/right-arrow.png";
import './reportForm.scss' import './reportForm.scss'
import "react-datepicker/dist/react-datepicker.css"; import "react-datepicker/dist/react-datepicker.css";
import { Navigation } from '../Navigation/Navigation'
const ReportForm = () => { const ReportForm = () => {
if(localStorage.getItem('role_status') === '18') { if(localStorage.getItem('role_status') === '18') {
@ -99,6 +100,7 @@ const ReportForm = () => {
return ( return (
<section className='report-form'> <section className='report-form'>
<ProfileHeader/> <ProfileHeader/>
<Navigation />
<div className='container'> <div className='container'>
<ProfileBreadcrumbs links={[{name: 'Главная', link: '/profile'}, <ProfileBreadcrumbs links={[{name: 'Главная', link: '/profile'},
{name: 'Ваша отчетность', link: '/profile/calendar'}, {name: 'Ваша отчетность', link: '/profile/calendar'},

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

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

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,36 +1,36 @@
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) { 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": case "1":
return ( return (
<div className="form-task__group"> <div className="form-task__group">
<textarea className='form-task__field' value={inputValue} <textarea className='form-task__field' value={inputValue}
onChange={handleChange}/> onChange={handleChange} />
</div> </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: default:
return ( return (
<div className="form-task__group" key={answer.id}> <div className="form-task__group" key={answer.id}>
<input className='form-task__check' type="checkbox" value={answer.answer_body} id={answer.id} <input className='form-task__check' type="radio" value={answer.answer_body} name={'radio'} id={answer.id}
onChange={handleChange}/> onChange={handleChange} />
<label htmlFor={answer.id}>{answer.answer_body}</label> <label htmlFor={answer.id}>{answer.answer_body}</label>
</div> </div>
) )

View File

@ -17,8 +17,8 @@ export const HeaderQuiz = ({header}) => {
}, [userId, dispatch]); }, [userId, dispatch]);
useEffect(() => { useEffect(() => {
apiRequest(`/user-questionnaire/questionnaires-list?user_id=${userId}`) // apiRequest(`/user-questionnaire/questionnaires-list?user_id=${userId}`)
.then(res => dispatch(setQuestionnairesList(res))) // .then(res => dispatch(setQuestionnairesList(res)))
}, [userId, dispatch]); }, [userId, dispatch]);
return ( 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 React, { useEffect, useState } from 'react'
import {useNavigate} from "react-router-dom" import { useNavigate } from "react-router-dom"
import {useSelector, useDispatch} from 'react-redux' import { useSelector, useDispatch } from 'react-redux'
import questionIcon from "../../../images/question.png"
import {Progressbar} from './ProgressbarQuiz' import { Progressbar } from './ProgressbarQuiz'
import {GetOptionTask} from './GetOptionTask' import { GetOptionTask } from './GetOptionTask'
import { import {
fetchUserAnswersMany, fetchUserAnswerOne, fetchGetAnswers, selectAnswer, selectedTest fetchUserAnswersMany, fetchUserAnswerOne, fetchGetAnswers, answersSelector, selectedTest, questionsSelector, setAnswers, setCompleteTest
} from './../../../redux/quizSlice' } from './../../../redux/quizSlice'
import './quiz.scss' import './quiz.scss'
import {apiRequest} from "../../../api/request"; import { apiRequest } from "../../../api/request";
import { HeaderQuiz } from './HeaderQuiz'
export const TaskQuiz = () => { export const TaskQuiz = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const dispatch = useDispatch(); const dispatch = useDispatch();
const listAnswers = useSelector(selectAnswer); const answers = useSelector(answersSelector);
const dataTest = useSelector(selectedTest); const questions = useSelector(questionsSelector);
const dataTest = useSelector(selectedTest);
const [index, setIndex] = useState(0); const [index, setIndex] = useState(0);
const [checkedValues, setCheckedValues] = useState([]); const [checkedValues, setCheckedValues] = useState([]);
const [stripValue, setStripValue] = useState(0); //const [stripValue, setStripValue] = useState(0);
const [inputValue, setInputValue] = useState(''); const [inputValue, setInputValue] = useState('');
const [questions, setQuestions] = useState([]);
const id = localStorage.getItem('id'); const id = localStorage.getItem('id');
useEffect(() => { 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}`) apiRequest(`/question/get-questions?uuid=${dataTest.uuid}`)
.then((response) => { .then((response) => {
console.log(response) console.log(response)
setQuestions(response);
dispatch(fetchGetAnswers(response[0].id)); dispatch(fetchGetAnswers(response[0].id));
setStripValue((+index + 1) * 100 / response.length) setStripValue((+index + 1) * 100 / response.length)
}) })
@ -41,43 +44,46 @@ export const TaskQuiz = () => {
const nextQuestion = async (e) => { const nextQuestion = async (e) => {
e.preventDefault(); e.preventDefault();
//Проверка на валидацию ответов //Проверка на валидацию ответов
if (checkedValues.length || inputValue) { if (!(checkedValues.length || inputValue)) {
switch (questions[index].question_type_id) { alert("Вы не ответили на вопрос")
case '3': return
dispatch(fetchUserAnswersMany(checkedValues));
break;
case '2':
case '1':
case '4':
dispatch(fetchUserAnswerOne(checkedValues));
break;
default:
break;
} }
//Проверка на существование следующего запроса //отправка ответов на сервер
if (index < questions.length - 1) { if (questions[index].question_type_id != 3) {
await dispatch(fetchGetAnswers(questions[index + 1].id)); //dispatch(fetchUserAnswerOne(checkedValues));
setIndex(prev => prev >= questions.length - 1 ? prev : prev + 1); } else {
setStripValue((prev => prev + (100 / questions.length))); 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([]); setCheckedValues([]);
setInputValue('') setInputValue('')
} else {
navigate(`/quiz/result`);
alert("Тест пройден!")
}
} else {
alert("Вы не ответили на вопрос")
}
}; };
const handleChange = (e) => { const handleChange = (e) => {
const checked = e.target.checked; const checked = e.target.checked;
switch (questions[index].question_type_id) {
case '3': 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, { checked ? setCheckedValues(prev => [...prev, {
user_id: id, user_id: id,
user_questionnaire_uuid: dataTest.uuid, user_questionnaire_uuid: dataTest.uuid,
@ -85,43 +91,26 @@ export const TaskQuiz = () => {
response_body: e.target.value response_body: e.target.value
}]) : }]) :
setCheckedValues(prev => [...prev.filter(item => item.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
}])
}
}; };
console.log('render task');
return ( return (
<React.StrictMode>
<Progressbar indexQuestion={index + 1} width={stripValue}/>
<div className="task"> <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 ? <div className="task__container">
<GetOptionTask <div className='task__header'>
type={1} <img src={questionIcon} alt="" />
inputValue={checkedValues.length ? checkedValues[0].response_body : ''} <h3 className="task__title quiz-title_h3">{questions[index].question_body}</h3>
handleChange={handleChange} </div>
/>
: <div className="task__body">
listAnswers.map((answer) => ( <form className='task__form form-task' onSubmit={nextQuestion}>
{
answers.map((answer) => (
<GetOptionTask <GetOptionTask
key={answer.id} key={answer.id}
type={questions[index].question_type_id} type={questions[index].question_type_id}
@ -131,18 +120,22 @@ export const TaskQuiz = () => {
)) ))
} }
<div className="form-task__buttons"> <div className="form-task__buttons">
{questions.length !== index + 1 && {/* {
<button type='submit' className='quiz-btn' index != 0 && <button type='submit' className='form-task__btn quiz-btn quiz-btn_back'
onClick={(e) => nextQuestion(e)}>Далее</button>} onClick={prevQuestion}>Назад</button>
{questions.length === index + 1 && } */}
<button onClick={(e) => nextQuestion(e)} {
className='quiz-btn quiz-btn_dark-green'>Завершить</button>} index != questions.length && <button onClick={nextQuestion}
className='form-task__btn quiz-btn'>Далее</button>
}
</div> </div>
</form> </form>
</div> </div>
</div> </div>
} }
</div> </div>
</React.StrictMode>
) )
}; };

View File

@ -1,20 +1,20 @@
@use 'sass:math'; @use "sass:math";
@import 'functions.scss'; @import "functions.scss";
$maxWidthContainer: 1123; $maxWidthContainer: 1123;
._container{ ._container {
max-width: 1123px; max-width: 1123px;
margin: 0 auto; margin: 0 auto;
padding: 0 10px; padding: 0 10px;
} }
.quiz-text{ .quiz-text {
font-size: 20px; font-size: 20px;
font-weight: 400; font-weight: 400;
line-height: math.div(30, 20); line-height: math.div(30, 20);
} }
.title{ .title {
color: #282828; color: #282828;
font-family: "GT Eesti Pro Display"; font-family: "GT Eesti Pro Display";
font-size: 33px; font-size: 33px;
@ -22,54 +22,57 @@ $maxWidthContainer: 1123;
line-height: math.div(48, 33); line-height: math.div(48, 33);
letter-spacing: 0.56px; letter-spacing: 0.56px;
} }
.subtitle{ .subtitle {
color: #373936; color: #373936;
font-family: "GT Eesti Pro Display"; font-family: "GT Eesti Pro Display";
font-size: 20px; font-size: 20px;
font-weight: 400; font-weight: 400;
line-height: math.div(25, 20); line-height: math.div(25, 20);
} }
.quiz-btn{ .quiz-btn {
display: flex; display: flex;
font-family: "Lab Grotesque";
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: 6px 5px 20px rgba(82, 151, 34, 0.21); // box-shadow: 6px 5px 20px rgba(82, 151, 34, 0.21);
border-radius: 23px; white-space: nowrap;
color: #ffffff; background: #406128;
border-radius: 44px;
color: #fff;
outline: none; outline: none;
border: 2px solid #52b709; //border: 2px solid #52b709;
background: #52b709; border: none;
transition: 0 all ease 0.8s; transition: 0 all ease 0.8s;
padding: 14px 38px; padding: 7px 51px;
line-height: 1;
font-family: "Muller Extra Bold";
font-weight: 400; font-weight: 400;
font-size: 16px; font-size: 14px;
text-decoration: none; text-decoration: none;
&:hover{ line-height: 200%;
text-decoration: none; // &:hover{
color: #52b709; // text-decoration: none;
background: #fff; // color: #52b709;
// background: #fff;
// }
&_back {
background: #dddddd;
color: #000;
} }
&_dark-green{ &_dark-green {
background-color: #1a310c; background-color: #1a310c;
border: 2px solid #1a310c; border: 2px solid #1a310c;
&:hover{ &:hover {
text-decoration: none; text-decoration: none;
color: #1a310c; color: #1a310c;
background: #fff; background: #fff;
} }
} }
&_restriction{
max-width: 131px;
}
} }
.quiz-title_h3{ .quiz-title_h3 {
color: #000000; font-family: "Lab Grotesque";
font-family: "GT Eesti Pro Display"; font-weight: 700;
font-size: 25px; font-size: 18px;
font-weight: 400; line-height: 156%;
line-height: math.div(30, 25); color: #52b709;
} }
//============================================= //=============================================
@ -80,213 +83,42 @@ $maxWidthContainer: 1123;
font-weight: 500; font-weight: 500;
} }
.header-quiz{ .task {
@include adaptiv-value("padding-top", 48, 30, 1);
@include adaptiv-value("padding-bottom", 85, 30, 1);
&__container{
max-width: $maxWidthContainer+px;
margin: 0 auto;
padding: 0 10px;
}
&__title-main{
font-family: "GT Eesti Pro Display";
font-size: 24px;
font-weight: bold;
@include adaptiv-value("margin-bottom", 80, 30, 1);
}
&__body{
display: flex;
flex-wrap: wrap;
row-gap: 30px;
margin-bottom: 39px;
}
&__body_interjacent{
align-items: center;
}
&__description{
max-width: 576px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
&__avatar{
position: relative;
display: block;
flex: 0 0 133px;
min-height: 133px;
margin-right: 31px;
img{
position: absolute;
width: 100%;
height: 100%;
object-fit: cover;
top: 0;
left: 0;
}
}
&__name-user{
color: #000000;
font-family: "GT Eesti Pro Display";
font-size: 16px;
font-weight: 400;
line-height: math.div(30, 20);
margin-right: 70px;
}
&__title{
color: #000000;
font-family: "GT Eesti Pro Display";
@include adaptiv-value("font-size", 25, 16, 1);
font-weight: 700;
line-height: math.div(36, 25);
position: relative;
&::before{
position: absolute;
content: "";
width: prc(316, 370);
height: 5px;
border-radius: 3px;
background-color: #54b611;
bottom: -26px;
}
}
}
.my-tests{
font-family: "GT Eesti Pro Display";
// @include adaptiv-value("padding-top", 85, 30, 1);
// @include adaptiv-value("padding-bottom", 85, 30, 1);
&__container{
max-width: $maxWidthContainer+px;
margin: 0 auto;
padding: 0 10px;
}
&__title{
color: #000000;
font-size: 25px;
font-weight: 400;
line-height: math.div(30, 25);
@include adaptiv-value("margin-bottom", 80, 30, 1);
}
&__items{
display: flex;
align-items: center;
flex-wrap: wrap;
margin: 0 -41px -42px;
}
}
.item-test{
flex: 0 1 50%;
padding: 0 41px;
margin: 0 0 42px 0;
@media (max-width: 760px) {
flex: 0 1 100%;
}
&__name-test{
color: #373936;
margin-bottom: 29px;
}
.active{
color: #54b611;
}
&__body{
}
}
.test-data{
display: flex;
align-items: center;
&__calendar{
padding: 13px 21px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 5px;
border: 1px solid #c4c4c4;
background-color: #f0f7e0;
font-weight: 400;
font-size: 13px;
display: flex;
align-items: center;
margin-right: 17px;
img{
display: block;
margin-right: 11px;
}
}
&__hr{
width: 28px;
height: 5px;
border-radius: 3px;
background-color: #54b611;
margin-right: 26px;
}
&__select{
max-width: 131px;
}
}
.progressbar{
max-width: $maxWidthContainer+px;
margin: 0 auto;
padding: 0 10px;
&__body{
display: flex;
align-items: center;
}
&__value{
flex: 0 0 39px;
height: 39px;
background-color: #5cb42b;
border-radius: 50%;
color: #fff;
letter-spacing: 1.1px;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
// flex: 1 1 auto;
margin-right: 19px;
}
&__strip{
flex: 1 1 auto;
height: 19px;
div{
border-radius: 10px;
height: 100%;
background-color: #5cb42b;
}
}
}
.task{
@include adaptiv-value("padding-top", 35, 10, 1); @include adaptiv-value("padding-top", 35, 10, 1);
&__container{ background: #fff;
max-width: $maxWidthContainer+px; border-radius: 12px;
&__container {
max-width: $maxWidthContainer + px;
margin: 0 auto; margin: 0 auto;
padding: 0 10px; padding: 31px 51px;
} }
&__title{ &__body {
margin-bottom: 39px; margin-left: 50px;
} }
&__form{ &__header {
display: flex;
align-items: center;
gap: 24px;
margin-bottom: 38px;
}
&__title {
}
&__form {
} }
} }
.form-task{ .form-task {
&__field{ &__field {
padding: 10px 15px; padding: 10px 15px;
font-size: 17px; font-size: 17px;
font-family: 'GT Eesti Pro Display'; font-family: "GT Eesti Pro Display";
letter-spacing: 0.3px; letter-spacing: 0.3px;
outline: none; outline: none;
border: 1px solid #52b709; border: none;
width: 100%; width: 100%;
resize: vertical; resize: vertical;
background: #eff1f3;
border-radius: 8px;
} }
&__check { &__check {
padding: 0; padding: 0;
@ -296,7 +128,7 @@ $maxWidthContainer: 1123;
display: none; display: none;
cursor: pointer; cursor: pointer;
} }
&__buttons{ &__buttons {
display: flex; display: flex;
gap: 56px; gap: 56px;
flex-wrap: wrap; flex-wrap: wrap;
@ -305,106 +137,600 @@ $maxWidthContainer: 1123;
} }
} }
.form-task__group{ .form-task__group {
display: block; display: block;
margin-bottom: 13px;
margin-bottom: 15px; font-family: "Lab Grotesque";
label { label {
color: #373936; font-weight: 400;
font-family: "GT Eesti Pro Display"; font-size: 15px;
font-size: 18px; line-height: 160%;
font-weight: 300; color: #000000;
line-height: math.div(28,18);
position: relative; position: relative;
margin: 0;
cursor: pointer; cursor: pointer;
&:before { &:before {
content:''; content: "";
-webkit-appearance: none; -webkit-appearance: none;
background-color: transparent; background-color: #eff1f3;
border: 2px solid #52b709; border: 2px solid #eff1f3;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05), inset 0px -15px 10px -12px rgba(0, 0, 0, 0.05); border-radius: 8px;
padding: 11px; width: 24px;
height: 24px;
display: inline-block; display: inline-block;
position: relative; position: relative;
vertical-align: middle; vertical-align: middle;
cursor: pointer; cursor: pointer;
margin-right: 30px; margin-right: 16px;
} }
} }
input[type="radio"] + label:before{ input[type="radio"]:checked + label::after {
border-radius: 50%;
}
input:checked + label:before{
background: #52b709;
}
input:checked + label:after {
content: ""; content: "";
display: block; display: block;
position: absolute; position: absolute;
top: 6px; top: 7.5px;
left: 6px;
width: 12px;
height: 12px;
background: #8dc63f;
border-radius: 50%;
}
input:checked + label:before {
background: #eff1f3;
}
input[type="checkbox"]:checked + label:after {
content: "";
display: block;
position: absolute;
top: 7px;
left: 9px; left: 9px;
width: 7px; width: 6px;
height: 14px; height: 13px;
border: solid #fff; border: solid #1c1243;
border-width: 0 2px 2px 0; border-width: 0 2px 2px 0;
transform: rotate(45deg); transform: rotate(45deg);
} }
} }
.instruction{ .instruction {
&__container{ &__container {
max-width: $maxWidthContainer+px; max-width: $maxWidthContainer + px;
margin: 0 auto; margin: 0 auto;
padding: 0 10px; padding: 0 10px;
} }
&__title{ &__title {
@include adaptiv-value("margin-bottom", 62, 20, 1); @include adaptiv-value("margin-bottom", 62, 20, 1);
} }
&__text{ &__text {
color: #373936; color: #373936;
font-family: "GT Eesti Pro Display"; font-family: "GT Eesti Pro Display";
font-size: 18px; font-size: 18px;
font-weight: 300; font-weight: 300;
line-height: math.div(28, 18); line-height: math.div(28, 18);
@include adaptiv-value("margin-bottom", 50, 20, 1); @include adaptiv-value("margin-bottom", 50, 20, 1);
span{ span {
color: #54b611; color: #54b611;
font-weight: 700; font-weight: 700;
} }
&_info{ &_info {
margin-bottom: 0; margin-bottom: 0;
} }
} }
&__info{ &__info {
display: flex; display: flex;
gap: 24px; gap: 24px;
align-items: center; align-items: center;
} }
&__icon{ &__icon {
width: 36px; width: 36px;
height: 33px; height: 33px;
} }
&__btn{ &__btn {
margin-bottom: 34px; margin-bottom: 34px;
} }
} }
.result{ .result {
font-family: "GT Eesti Pro Display"; font-family: "GT Eesti Pro Display";
&__body{ &__body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: flex-start; align-items: flex-start;
gap: 10px; gap: 10px;
} }
&__text{ &__text {
font-size: 24px; font-size: 24px;
font-weight: bold; font-weight: bold;
} }
&__score{ &__score {
color: #5cb42b; color: #5cb42b;
} }
} }
.selected-category {
background: #ffffff;
border-radius: 12px;
&__container {
display: flex;
align-items: center;
padding: 35px;
flex-wrap: wrap;
@media (max-width: 723px) {
justify-content: space-between;
row-gap: 25px;
}
}
&__title {
font-weight: 700;
font-size: 18px;
line-height: 156%;
color: #406128;
margin: 0 32px 0 0;
}
&__category {
display: flex;
align-items: center;
gap: 18px;
margin: 0 44px 0 0;
}
&__img {
height: 48px;
flex: 0 0 48px;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
&__title-category {
font-weight: 500;
font-size: 18px;
line-height: 122%;
color: #000000;
}
&__button {
background: #e1fccf;
border-radius: 44px;
font-weight: 500;
font-size: 14px;
line-height: 229%;
border: none;
padding: 5px 20px;
color: #000000;
@media (max-width: 723px) {
flex: 1 1 auto;
text-align: center;
}
&:hover {
text-decoration: none;
color: inherit;
}
}
}
.card-introduction {
padding: 0px 25px;
margin: 0px 0px 50px 0px;
flex: 0 1 33.333%;
@media (max-width: 850px) {
flex: 0 1 50%;
}
@media (max-width: 850px) {
flex: 1 1 auto;
}
&__body {
display: flex;
gap: 20px;
}
&__title {
font-weight: 900;
font-size: 14px;
line-height: 171%;
color: #52b709;
margin: 0 0 30px 0;
}
&__icon {
flex: 0 0 25px;
height: 30px;
img {
width: 100%;
height: 100%;
}
}
&__text {
font-weight: 400;
font-size: 12px;
line-height: 200%;
color: #000000;
}
}
.quiz-passing-information {
background: #fff;
border-radius: 12px;
&__container {
// @media (max-width: 600px) {
// display: block;
// }
// @media (max-width: 600px) {
// display: block;
// }
// @media (max-width: 600px) {
// display: block;
// }
}
&__main {
//flex: 1 1 auto;
padding: 35px 45px;
height: 100%;
display: grid;
grid-template-columns: 1fr 1.2fr 1.2fr 0.6fr;
@media (max-width: 900px) {
grid-template-columns: 1fr 1fr;
grid-gap: 20px;
}
@media (max-width: 500px) {
grid-template-columns: 1fr;
}
}
&__icon {
flex: 0 0 32px;
height: 35px;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
&__text {
font-weight: 400;
font-size: 12px;
line-height: 167%;
color: #6f6f6f;
span {
color: #111112;
font-weight: 700;
}
}
&__timer,
&__attempt {
display: flex;
gap: 23px;
align-items: center;
}
&__specialization {
display: flex;
align-items: center;
gap: 15px;
}
&__specialization-title {
font-weight: 500;
font-size: 18px;
line-height: 122%;
color: #000000;
word-break: break-word;
}
&__button {
font-size: 18px;
max-width: 174px;
height: 46px;
font-weight: 500;
}
}
.block-text {
border-radius: 12px;
background: #fff;
text-align: center;
width: 100%;
padding: 18px 0;
font-weight: 700;
font-size: 18px;
line-height: 156%;
color: #000000;
a {
color: #406128;
}
}
.card-available-test {
position: relative;
background: #ffffff;
border-radius: 12px;
text-decoration: none;
cursor: pointer;
transition: all 0.3s ease;
&__container {
display: block;
@include adaptiv-value("padding-top", 30, 20, 1);
@include adaptiv-value("padding-bottom", 30, 20, 1);
@include adaptiv-value("padding-right", 28, 18, 1);
@include adaptiv-value("padding-left", 28, 18, 1);
}
&:hover {
box-shadow: 6px 5px 20px rgb(87 98 80 / 21%);
transform: scale(1.02);
text-decoration: none;
}
&__top-head {
display: flex;
align-items: center;
gap: 19px;
margin: 0 0 24px 0;
}
&__title {
font-weight: 500;
font-size: 18px;
line-height: 122%;
color: #000000;
max-width: 128px;
margin: 0;
}
&__info {
display: flex;
justify-content: space-between;
align-items: center;
p {
flex: 0 1 66%;
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;
}
}
&__finished {
background: rgba(255, 255, 255, 0.76);
mix-blend-mode: normal;
border: 3px solid #52b709;
border-radius: 12px;
padding: 13px 16px;
position: absolute;
bottom: 0;
left: 0;
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
gap: 10px;
p {
font-weight: 500;
font-size: 14px;
line-height: 129%;
color: #000000;
}
a {
&:hover {
text-decoration: none;
color: #fff;
}
display: block;
background: #52b709;
border-radius: 44px;
font-weight: 500;
font-size: 15px;
line-height: 213%;
color: #ffffff;
padding: 9px 25px;
white-space: nowrap;
outline: none;
border: none;
}
}
}
.block-completed-test {
background: #ffffff;
border-radius: 12px;
// .block-completed-test__container
&__container {
padding: 41px 35px 29px 59px;
display: flex;
align-items: center;
flex-wrap: wrap;
}
// .block-completed-test__img
&__img {
flex: 0 0 54px;
height: 60px;
margin: 0 34px 0 0;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
// .block-completed-test__title
&__title {
font-weight: 700;
font-size: 18px;
line-height: 156%;
margin: 0 60px 0 0;
color: #52b709;
}
// .block-completed-test__text
&__text {
max-width: 386px;
margin: 0 26px 0 0;
font-weight: 400;
font-size: 12px;
line-height: 200%;
color: #000000;
}
// .block-completed-test__button
&__button {
display: block;
}
}
.report {
// .report__row
&__row {
display: flex;
align-items: center;
width: 100%;
height: 100%;
flex-wrap: wrap;
@media (max-width: 600px) {
display: block;
}
}
// .report__column
&__column {
&:first-child {
background: #ffffff;
flex: 0 1 22%;
border-radius: 12px;
@media (max-width: 1000px) {
flex: 0 1 50%;
}
}
height: 128px;
border-radius: 0px 12px 0px 0px;
flex: 0 1 26%;
display: flex;
justify-content: center;
align-items: center;
background: #e1fccf;
gap: 21px;
&:last-child {
flex-direction: column;
gap: 0;
}
@media (max-width: 1000px) {
flex: 0 1 50%;
}
}
// .report__job-title
&__job-title {
font-weight: 500;
font-size: 18px;
line-height: 122%;
color: #000000;
}
// .report__value
&__value {
font-weight: 700;
font-size: 52px;
line-height: 56%;
color: #52b709;
// .report__value_false
&_false {
color: #5b6871;
}
}
// .report__text
&__text {
font-weight: 400;
font-size: 16px;
line-height: 138%;
color: #000000;
}
// .report__status-text
&__status-text {
font-weight: 400;
font-size: 12px;
line-height: 200%;
color: #000000;
width: 50%;
}
// .report__status
&__status {
background: #1458dd;
border-radius: 44px;
font-weight: 500;
font-size: 16px;
line-height: 200%;
color: #ffffff;
padding: 7px 34px;
max-width: 154px;
}
}
.alert-result {
display: flex;
align-items: center;
@include adaptiv-value("padding-top", 39, 20, 1);
@include adaptiv-value("padding-bottom", 39, 20, 1);
@include adaptiv-value("padding-right", 61, 20, 1);
@include adaptiv-value("padding-left", 61, 20, 1);
background: #fff;
gap: 30px;
border-radius: 10px;
@media (max-width: 900px) {
flex-wrap: wrap;
}
&__column {
display: flex;
@include adaptiv-value("gap", 44, 14, 1);
align-items: center;
@media (max-width: 900px) {
flex: 1 1 auto;
}
}
&__icon {
width: 39px;
height: 39px;
}
&__text {
font-weight: 700;
@include adaptiv-value("font-size", 18, 14, 1);
line-height: 156%;
word-break: break-word;
}
&__button {
width: 231px;
@include adaptiv-value("font-size", 16, 14, 1);
@media (max-width: 900px) {
width: 100%;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
src/images/note.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

BIN
src/images/question.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 B

BIN
src/images/quiz/accempt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
src/images/quiz/success.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
src/images/quiz/timer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -19,7 +19,7 @@ code {
} }
h1 { h1 {
display: none;
} }
.container { .container {

View File

@ -6,7 +6,7 @@
.auth { .auth {
&__wrapper { &__wrapper {
background: #FFFFFF; background: #ffffff;
border-radius: 12px; border-radius: 12px;
padding: 50px 0 35px 56px; padding: 50px 0 35px 56px;
margin-top: 40px; margin-top: 40px;
@ -37,7 +37,7 @@
input { input {
margin-bottom: 30px; margin-bottom: 30px;
background: #EFF2F7; background: #eff2f7;
border-radius: 8px; border-radius: 8px;
min-width: 300px; min-width: 300px;
width: 100%; width: 100%;
@ -58,7 +58,7 @@
} }
button { button {
background: #52B709; background: #52b709;
border-radius: 44px; border-radius: 44px;
max-width: 130px; max-width: 130px;
border: none; border: none;
@ -85,7 +85,7 @@
text-align: center; text-align: center;
span { span {
color: #52B709; color: #52b709;
} }
} }
@ -96,13 +96,19 @@
} }
&__categoriesWrapper { &__categoriesWrapper {
display: flex;
position: relative; position: relative;
margin-top: 200px; margin-top: 200px;
flex-wrap: wrap;
row-gap: 24px;
column-gap: 21px;
width: 100%; 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;
}
} }
} }

View File

@ -11,6 +11,7 @@ import {ProfileHeader} from "../../components/ProfileHeader/ProfileHeader";
import {ProfileBreadcrumbs} from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs" import {ProfileBreadcrumbs} from "../../components/ProfileBreadcrumbs/ProfileBreadcrumbs"
import {apiRequest} from "../../api/request"; import {apiRequest} from "../../api/request";
import {Navigate} from "react-router-dom"; import {Navigate} from "react-router-dom";
import { Navigation } from '../../components/Navigation/Navigation'
const Home = () => { const Home = () => {
@ -58,6 +59,7 @@ const Home = () => {
return ( return (
<> <>
<ProfileHeader/> <ProfileHeader/>
<Navigation />
<div className="catalog"> <div className="catalog">
<div className='container'> <div className='container'>
<ProfileBreadcrumbs links={[ <ProfileBreadcrumbs links={[

View File

@ -12,6 +12,7 @@ import documentsImg from "../../images/partnerAddRequestThirdInfo.png"
import {Navigate} from "react-router-dom"; import {Navigate} from "react-router-dom";
import './partnerAddRequest.scss' import './partnerAddRequest.scss'
import { Navigation } from '../../components/Navigation/Navigation';
export const PartnerAddRequest = () => { export const PartnerAddRequest = () => {
if(localStorage.getItem('role_status') !== '18') { if(localStorage.getItem('role_status') !== '18') {
@ -20,6 +21,7 @@ export const PartnerAddRequest = () => {
return ( return (
<div className='partnerAddRequest'> <div className='partnerAddRequest'>
<ProfileHeader /> <ProfileHeader />
<Navigation />
<div className='container'> <div className='container'>
<ProfileBreadcrumbs links={[ <ProfileBreadcrumbs links={[
{name: 'Главная', link: '/profile'}, {name: 'Главная', link: '/profile'},

View File

@ -12,6 +12,7 @@ import personImg from "../../images/mokPerson.png"
import deleteBtn from "../../images/deleteBtn.png" import deleteBtn from "../../images/deleteBtn.png"
import './partnerBid.scss' import './partnerBid.scss'
import { Navigation } from '../../components/Navigation/Navigation';
export const PartnerBid = () => { export const PartnerBid = () => {
if(localStorage.getItem('role_status') !== '18') { if(localStorage.getItem('role_status') !== '18') {
@ -37,6 +38,7 @@ export const PartnerBid = () => {
return ( return (
<div className='partnerBid'> <div className='partnerBid'>
<ProfileHeader /> <ProfileHeader />
<Navigation />
<div className='container'> <div className='container'>
<ProfileBreadcrumbs links={[ <ProfileBreadcrumbs links={[
{name: 'Главная', link: '/profile'}, {name: 'Главная', link: '/profile'},

View File

@ -9,6 +9,7 @@ import {Footer} from "../../components/Footer/Footer";
import cursorImg from "../../images/cursorImg.png" import cursorImg from "../../images/cursorImg.png"
import './partnerRequests.scss' import './partnerRequests.scss'
import { Navigation } from '../../components/Navigation/Navigation';
export const PartnerRequests = () => { export const PartnerRequests = () => {
if(localStorage.getItem('role_status') !== '18') { if(localStorage.getItem('role_status') !== '18') {
@ -35,6 +36,7 @@ export const PartnerRequests = () => {
return ( return (
<div className='partnerRequests'> <div className='partnerRequests'>
<ProfileHeader /> <ProfileHeader />
<Navigation />
<div className='container'> <div className='container'>
<ProfileBreadcrumbs links={[ <ProfileBreadcrumbs links={[
{name: 'Главная', link: '/profile'}, {name: 'Главная', link: '/profile'},

View File

@ -7,11 +7,13 @@ import { ProfileHeader } from "../../components/ProfileHeader/ProfileHeader";
import kontur from "../../images/konturLogo.png"; import kontur from "../../images/konturLogo.png";
import astral from "../../images/astralLogo.png"; import astral from "../../images/astralLogo.png";
import "./partnerSettings.scss"; import "./partnerSettings.scss";
import { Navigation } from "../../components/Navigation/Navigation";
export const PartnerSettings = () => { export const PartnerSettings = () => {
return ( return (
<div className="settings"> <div className="settings">
<ProfileHeader /> <ProfileHeader />
<Navigation />
<div className="container settings__page"> <div className="container settings__page">
<ProfileBreadcrumbs <ProfileBreadcrumbs
links={[ links={[

View File

@ -15,6 +15,7 @@ import lockDone from "./Images/lockDone.svg"
import avatarMok from "./Images/avatarMok.png" import avatarMok from "./Images/avatarMok.png"
import './partnerTreaties.scss' import './partnerTreaties.scss'
import { Navigation } from '../../components/Navigation/Navigation';
export const PartnerTreaties = () => { export const PartnerTreaties = () => {
const [toggleTab, setToggleTab] = useState(1); const [toggleTab, setToggleTab] = useState(1);
@ -25,6 +26,7 @@ export const PartnerTreaties = () => {
return( return(
<div className="treaties"> <div className="treaties">
<ProfileHeader/> <ProfileHeader/>
<Navigation />
<div className="container"> <div className="container">
<ProfileBreadcrumbs <ProfileBreadcrumbs
links={[ links={[

View File

@ -22,6 +22,7 @@ import rightArrow from "../../images/arrowRight.png"
import avatarImg from "../PartnerEmployees/avatarMok.png"; import avatarImg from "../PartnerEmployees/avatarMok.png";
import "./partnerСategories.scss" import "./partnerСategories.scss"
import { Navigation } from '../../components/Navigation/Navigation';
export const PartnerCategories = () => { export const PartnerCategories = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -130,6 +131,7 @@ export const PartnerCategories = () => {
return ( return (
<div className="partnerCategories"> <div className="partnerCategories">
<ProfileHeader /> <ProfileHeader />
<Navigation />
<div className="container"> <div className="container">
<ProfileBreadcrumbs links={[ <ProfileBreadcrumbs links={[
{name: 'Главная', link: '/profile'}, {name: 'Главная', link: '/profile'},

View File

@ -1,11 +1,13 @@
import React from 'react'; import React from 'react';
import {ProfileHeader} from "../../components/ProfileHeader/ProfileHeader"; import {ProfileHeader} from "../../components/ProfileHeader/ProfileHeader";
import { Navigation } from '../../components/Navigation/Navigation';
export const Payouts = () => { export const Payouts = () => {
return ( return (
<div className='payouts'> <div className='payouts'>
<ProfileHeader /> <ProfileHeader />
<Navigation />
</div> </div>
) )
}; };

View File

@ -19,6 +19,8 @@ import settingIcon from "../../images/settingIcon.png"
import rightArrow from "../../images/arrowRight.png" import rightArrow from "../../images/arrowRight.png"
import './profile.scss' import './profile.scss'
import { CardControl } from '../../components/CardControl/CardControl';
import { Navigation } from '../../components/Navigation/Navigation';
export const Profile = () => { export const Profile = () => {
@ -27,19 +29,19 @@ export const Profile = () => {
const [profileItemsInfo] = useState({ const [profileItemsInfo] = useState({
developer: [ developer: [
{ {
path: '/calendar', path: 'profile/calendar',
img: reportsIcon, img: reportsIcon,
title: 'Ваша отчетность', title: 'Ваша отчетность',
description: '<span></span>Отработанных в этом месяце часов' description: '<span></span>Отработанных в этом месяце часов'
}, },
{ {
path: '/summary', path: 'profile/summary',
img: summaryIcon, img: summaryIcon,
title: 'Данные и резюме', title: 'Данные и резюме',
description: 'Ваше резюме<br/><span>заполнено</span>' description: 'Ваше резюме<br/><span>заполнено</span>'
}, },
{ {
path: '/tracker', path: 'profile/tracker',
img: timerIcon, img: timerIcon,
title: 'Трекер времени', title: 'Трекер времени',
description: 'Сколько времени занимает<br/> выполнение задач' description: 'Сколько времени занимает<br/> выполнение задач'
@ -51,7 +53,7 @@ export const Profile = () => {
description: 'У вас <span>подтвержден</span><br/> статус самозанятого' description: 'У вас <span>подтвержден</span><br/> статус самозанятого'
}, },
{ {
path: '/settings', path: 'profile/settings',
img: settingIcon, img: settingIcon,
title: 'Настройки аккаунта', title: 'Настройки аккаунта',
description: 'Перейдите чтобы начать<br/> редактирование' description: 'Перейдите чтобы начать<br/> редактирование'
@ -59,31 +61,31 @@ export const Profile = () => {
], ],
partner: [ partner: [
{ {
path: '/requests', path: 'profile/requests',
img: reportsIcon, img: reportsIcon,
title: 'Запросы и открытые позиции', title: 'Запросы и открытые позиции',
description: '<span>У вас 2 вакансии<br/></span>открытые от лица компании' description: '<span>У вас 2 вакансии<br/></span>открытые от лица компании'
}, },
{ {
path: '/categories', path: 'profile/categories',
img: summaryIcon, img: summaryIcon,
title: 'Данные персонала', title: 'Данные персонала',
description: 'Наши специалисты <br/><span>уже работающие у вас</span>' description: 'Наши специалисты <br/><span>уже работающие у вас</span>'
}, },
{ {
path: '/tracker', path: 'profile/tracker',
img: timerIcon, img: timerIcon,
title: 'Трекер времени', title: 'Трекер времени',
description: 'Контроль времени и<br/> выполнение задач' description: 'Контроль времени и<br/> выполнение задач'
}, },
{ {
path: '/treaties', path: 'profile/treaties',
img: paymentIcon, img: paymentIcon,
title: 'Договора и отчетность', title: 'Договора и отчетность',
description: 'Ключевые условия<br/> договора' description: 'Ключевые условия<br/> договора'
}, },
{ {
path: '/settings', path: 'profile/settings',
img: settingIcon, img: settingIcon,
title: 'Настройки аккаунта', title: 'Настройки аккаунта',
description: 'Перейдите чтобы начать<br/> редактирование' description: 'Перейдите чтобы начать<br/> редактирование'
@ -94,6 +96,7 @@ export const Profile = () => {
return ( return (
<div className='profile'> <div className='profile'>
<ProfileHeader/> <ProfileHeader/>
<Navigation />
<div className='container'> <div className='container'>
<ProfileBreadcrumbs links={[{name: 'Главная', link: '/profile'}]} /> <ProfileBreadcrumbs links={[{name: 'Главная', link: '/profile'}]} />
<h2 className='profile__title'> <h2 className='profile__title'>
@ -118,18 +121,13 @@ export const Profile = () => {
<div className='profile__items'> <div className='profile__items'>
{ {
profileItemsInfo[user].map((item, index) => { profileItemsInfo[user].map((item, index) => {
return <Link key={index} to={`/profile${item.path}`} className='item'> return <CardControl
<div className='item__about'> description={item.description}
<img src={item.img} alt='itemImg'/> img={item.img}
<h3>{item.title}</h3> path={item.path}
</div> title={item.title}
<div className='item__info'> key={index}
<p dangerouslySetInnerHTML={{__html: item.description}}></p> />
<div className='item__infoLink'>
<img src={rightArrow} alt='arrow'/>
</div>
</div>
</Link>
}) })
} }
</div> </div>

View File

@ -49,79 +49,79 @@
justify-content: center; justify-content: center;
} }
.item { // .item {
max-width: 353px; // max-width: 353px;
width: 100%; // width: 100%;
padding: 35px 45px 15px 30px; // padding: 35px 45px 15px 30px;
background: #FFFFFF; // background: #FFFFFF;
border-radius: 12px; // border-radius: 12px;
text-decoration: none; // text-decoration: none;
cursor: pointer; // cursor: pointer;
transition: all 0.3s ease; // transition: all 0.3s ease;
&:hover { // &:hover {
box-shadow: 6px 5px 20px rgb(87 98 80 / 21%); // box-shadow: 6px 5px 20px rgb(87 98 80 / 21%);
transform: scale(1.02); // transform: scale(1.02);
} // }
@media (max-width: 1175px) { // @media (max-width: 1175px) {
width: 48%; // width: 48%;
max-width: none; // max-width: none;
} // }
@media (max-width: 925px) { // @media (max-width: 925px) {
width: 100%; // width: 100%;
padding: 15px 25px; // padding: 15px 25px;
} // }
&__about { // &__about {
display: flex; // display: flex;
column-gap: 20px; // column-gap: 20px;
align-items: center; // align-items: center;
margin-bottom: 30px; // margin-bottom: 30px;
@media (max-width: 925px) { // @media (max-width: 925px) {
margin-bottom: 15px; // margin-bottom: 15px;
} // }
h3 { // h3 {
color: #000000; // color: #000000;
font-weight: 500; // font-weight: 500;
font-size: 18px; // font-size: 18px;
line-height: 22px; // line-height: 22px;
max-width: 125px; // max-width: 125px;
margin-bottom: 0; // margin-bottom: 0;
} // }
} // }
&__info { // &__info {
display: flex; // display: flex;
justify-content: space-between; // justify-content: space-between;
align-items: center; // align-items: center;
p { // p {
font-weight: 700; // font-weight: 700;
font-size: 12px; // font-size: 12px;
line-height: 20px; // line-height: 20px;
color: #000000; // color: #000000;
margin-bottom: 0; // margin-bottom: 0;
span { // span {
color: #52B709; // color: #52B709;
font-weight: 700; // font-weight: 700;
} // }
} // }
&Link { // &Link {
width: 48px; // width: 48px;
height: 48px; // height: 48px;
background: #DDEEC6; // background: #DDEEC6;
border-radius: 50px; // border-radius: 50px;
display: flex; // display: flex;
justify-content: center; // justify-content: center;
align-items: center; // align-items: center;
} // }
} // }
} // }
} }
.container { .container {

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

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

View File

@ -13,6 +13,7 @@ import gitImgItem from "../../images/gitItemImg.png"
import './summary.scss' import './summary.scss'
import {apiRequest} from "../../api/request"; import {apiRequest} from "../../api/request";
import {Navigate} from "react-router-dom"; import {Navigate} from "react-router-dom";
import { Navigation } from '../../components/Navigation/Navigation';
export const Summary = () => { export const Summary = () => {
if(localStorage.getItem('role_status') === '18') { if(localStorage.getItem('role_status') === '18') {
@ -29,6 +30,7 @@ export const Summary = () => {
return ( return (
<div className='summary'> <div className='summary'>
<ProfileHeader/> <ProfileHeader/>
<Navigation />
<div className='container'> <div className='container'>
<div className='summary__content'> <div className='summary__content'>
<ProfileBreadcrumbs links={[ <ProfileBreadcrumbs links={[

View File

@ -22,6 +22,7 @@ import noProjects from "../../images/noProjects.png"
import arrow from '../../images/arrowCalendar.png' import arrow from '../../images/arrowCalendar.png'
import "./tracker.scss"; import "./tracker.scss";
import { Navigation } from "../../components/Navigation/Navigation";
export const Tracker = () => { export const Tracker = () => {
const [toggleTab, setToggleTab] = useState(1); const [toggleTab, setToggleTab] = useState(1);
@ -524,6 +525,7 @@ export const Tracker = () => {
return ( return (
<div className="tracker"> <div className="tracker">
<ProfileHeader /> <ProfileHeader />
<Navigation />
<div className="container"> <div className="container">
<div className="tracker__content"> <div className="tracker__content">
<ProfileBreadcrumbs links={[ <ProfileBreadcrumbs links={[

View File

@ -15,6 +15,7 @@ import {apiRequest} from "../../api/request";
import {getCorrectDate, getCreatedDate, hourOfNum} from '../../components/Calendar/calendarHelper' import {getCorrectDate, getCreatedDate, hourOfNum} from '../../components/Calendar/calendarHelper'
import './viewReport.scss' import './viewReport.scss'
import { Navigation } from '../../components/Navigation/Navigation';
export const ViewReport = () => { export const ViewReport = () => {
if(localStorage.getItem('role_status') === '18') { if(localStorage.getItem('role_status') === '18') {
@ -78,6 +79,7 @@ export const ViewReport = () => {
return ( return (
<div className='viewReport'> <div className='viewReport'>
<ProfileHeader/> <ProfileHeader/>
<Navigation />
<div className='container'> <div className='container'>
<div className='viewReport__info'> <div className='viewReport__info'>
<ProfileBreadcrumbs links={[{name: 'Главная', link: '/profile'}, <ProfileBreadcrumbs links={[{name: 'Главная', link: '/profile'},

View File

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

View File

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

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

View File

@ -1,17 +1,144 @@
import React from 'react' import React, { useEffect, useState } from 'react'
import {HeaderQuiz} from "../../components/features/quiz/HeaderQuiz" import { useDispatch, useSelector } from "react-redux";
import {MyTestsQuiz} from "../../components/features/quiz/MyTestsQuiz" import { questionnairesSelector, setQuestionnaires } from "../../redux/quizSlice";
import {useSelector} from "react-redux"; import { ProfileHeader } from '../../components/ProfileHeader/ProfileHeader';
import {selectQuestionnairesOfUser} from "../../redux/quizSlice"; 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 = () => { 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=${107}`)
.then(res => dispatch(setQuestionnaires(res)))
}, [])
return ( return (
<> <div className='quiz-page'>
<HeaderQuiz header={true}/> <ProfileHeader />
<MyTestsQuiz listTests={allTests}/> <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>
) )
} }

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

View File

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

View File

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

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

View File

@ -4,14 +4,76 @@ import {apiRequest} from "../api/request";
const initialState = { const initialState = {
// questions: [], answers: [
answer: [], {
"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, result: null,
isLoading: false, isLoading: false,
dataQuestionnairesOfUser: [], dataQuestionnairesOfUser: [],
passedTests: [], passedTests: [],
selectedTest: null,
userInfo: null userInfo: null,
}; };
export const setUserInfo = createAsyncThunk( export const setUserInfo = createAsyncThunk(
'userInfo', 'userInfo',
@ -47,13 +109,15 @@ export const quizSlice = createSlice({
name: 'quiz', name: 'quiz',
initialState, initialState,
reducers: { reducers: {
setQuestionnairesList: (state, action) => { setQuestionnaires: (state, action) => {
state.dataQuestionnairesOfUser = action.payload; state.questionnaires = action.payload;
state.passedTests = action.payload.filter(item => item.status === 2)
}, },
setSelectedTest: (state, action) => { setSelectedTest: (state, action) => {
state.selectedTest = action.payload state.selectedTest = action.payload
}, },
setCompleteTest: (state, action) => {
state.completedTest = true
}
}, },
extraReducers: { extraReducers: {
[setUserInfo.fulfilled]: (state, action) => { [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 answersSelector = (state) => state.quiz.answers;
export const selectQuestionnairesOfUser = (state) => state.quiz.dataQuestionnairesOfUser; 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 selectResult = (state) => state.quiz.result;
export const selectedTest = (state) => state.quiz.selectedTest; export const selectedTest = (state) => state.quiz.selectedTest;
export const selectPassedTests = (state) => state.quiz.passedTests; export const selectPassedTests = (state) => state.quiz.passedTests;
export const selectUserInfo = (state) => state.quiz.userInfo; export const selectUserInfo = (state) => state.quiz.userInfo;
export default quizSlice.reducer; export default quizSlice.reducer;